Merge "Don't use pow for distance calculation" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bd66c1c..720bfc0 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -358,7 +358,7 @@
     visibility: ["//visibility:private"],
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":api-stubs-docs-non-updatable"],
@@ -368,7 +368,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs.system",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":system-api-stubs-docs-non-updatable"],
@@ -378,7 +378,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs.module_lib",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":module-lib-api-stubs-docs-non-updatable"],
@@ -392,7 +392,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android-non-updatable.stubs.test",
     defaults: ["android-non-updatable_defaults_stubs_current"],
     srcs: [":test-api-stubs-docs-non-updatable"],
@@ -415,7 +415,7 @@
     defaults_visibility: ["//frameworks/base/services"],
 }
 
-java_library_static {
+java_library {
     name: "android_stubs_current",
     static_libs: modules_public_stubs + [
         "android-non-updatable.stubs",
@@ -424,7 +424,7 @@
     defaults: ["android.jar_defaults"],
 }
 
-java_library_static {
+java_library {
     name: "android_system_stubs_current",
     static_libs: modules_system_stubs + [
         "android-non-updatable.stubs.system",
@@ -450,7 +450,7 @@
     ],
 }
 
-java_library_static {
+java_library {
     name: "android_test_stubs_current",
     // Modules do not have test APIs, but we want to include their SystemApis, like we include
     // the SystemApi of framework-non-updatable-sources.
@@ -467,7 +467,7 @@
     },
 }
 
-java_library_static {
+java_library {
     name: "android_module_lib_stubs_current",
     defaults: [
         "android.jar_defaults",
@@ -531,7 +531,7 @@
     visibility: ["//visibility:private"],
 }
 
-java_library_static {
+java_library {
     name: "hwbinder.stubs",
     sdk_version: "core_current",
     libs: ["framework-annotations-lib"],
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index aafeb48..54e47cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -43,6 +43,11 @@
 import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
 import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
 import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_EXACT_PERMISSION_REVOKED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_PI_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -116,6 +121,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.LocalLog;
+import com.android.internal.util.RingBuffer;
 import com.android.internal.util.StatLogger;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.AppStateTracker;
@@ -160,6 +166,7 @@
 public class AlarmManagerService extends SystemService {
     private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
     private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
+    private static final int REMOVAL_HISTORY_SIZE_PER_UID = 10;
     static final int TIME_CHANGED_MASK = 1 << 16;
     static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK | ELAPSED_REALTIME_WAKEUP_MASK;
 
@@ -238,6 +245,7 @@
     AppWakeupHistory mAppWakeupHistory;
     AppWakeupHistory mAllowWhileIdleHistory;
     private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
+    private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
     ClockReceiver mClockReceiver;
     final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
     IBinder.DeathRecipient mListenerDeathRecipient;
@@ -392,6 +400,57 @@
         }
     }
 
+    static class RemovedAlarm {
+        static final int REMOVE_REASON_UNDEFINED = 0;
+        static final int REMOVE_REASON_ALARM_CANCELLED = 1;
+        static final int REMOVE_REASON_EXACT_PERMISSION_REVOKED = 2;
+        static final int REMOVE_REASON_DATA_CLEARED = 3;
+        static final int REMOVE_REASON_PI_CANCELLED = 4;
+
+        final String mTag;
+        final long mWhenRemovedElapsed;
+        final long mWhenRemovedRtc;
+        final int mRemoveReason;
+
+        RemovedAlarm(Alarm a, int removeReason, long nowRtc, long nowElapsed) {
+            mTag = a.statsTag;
+            mRemoveReason = removeReason;
+            mWhenRemovedRtc = nowRtc;
+            mWhenRemovedElapsed = nowElapsed;
+        }
+
+        static final boolean isLoggable(int reason) {
+            // We don't want to log meaningless reasons. This also gives a way for callers to
+            // opt out of logging, e.g. when replacing an alarm.
+            return reason != REMOVE_REASON_UNDEFINED;
+        }
+
+        static final String removeReasonToString(int reason) {
+            switch (reason) {
+                case REMOVE_REASON_ALARM_CANCELLED:
+                    return "alarm_cancelled";
+                case REMOVE_REASON_EXACT_PERMISSION_REVOKED:
+                    return "exact_alarm_permission_revoked";
+                case REMOVE_REASON_DATA_CLEARED:
+                    return "data_cleared";
+                case REMOVE_REASON_PI_CANCELLED:
+                    return "pi_cancelled";
+                default:
+                    return "unknown:" + reason;
+            }
+        }
+
+        void dump(IndentingPrintWriter pw, long nowElapsed, SimpleDateFormat sdf) {
+            pw.print("[tag", mTag);
+            pw.print("reason", removeReasonToString(mRemoveReason));
+            pw.print("elapsed=");
+            TimeUtils.formatDuration(mWhenRemovedElapsed, nowElapsed, pw);
+            pw.print(" rtc=");
+            pw.print(sdf.format(new Date(mWhenRemovedRtc)));
+            pw.println("]");
+        }
+    }
+
     /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
@@ -1691,7 +1750,7 @@
 
     void removeImpl(PendingIntent operation, IAlarmListener listener) {
         synchronized (mLock) {
-            removeLocked(operation, listener);
+            removeLocked(operation, listener, REMOVE_REASON_UNDEFINED);
         }
     }
 
@@ -1812,7 +1871,7 @@
                     + " -- package not allowed to start");
             return;
         }
-        removeLocked(operation, directReceiver);
+        removeLocked(operation, directReceiver, REMOVE_REASON_UNDEFINED);
         incrementAlarmCount(a.uid);
         setImplLocked(a);
         MetricsHelper.pushAlarmScheduled(a);
@@ -2106,7 +2165,7 @@
         @Override
         public void removeAlarmsForUid(int uid) {
             synchronized (mLock) {
-                removeLocked(uid);
+                removeLocked(uid, REMOVE_REASON_DATA_CLEARED);
             }
         }
 
@@ -2342,7 +2401,7 @@
                 return;
             }
             synchronized (mLock) {
-                removeLocked(operation, listener);
+                removeLocked(operation, listener, REMOVE_REASON_ALARM_CANCELLED);
             }
         }
 
@@ -2688,6 +2747,7 @@
 
             pw.println("Allow while idle history:");
             mAllowWhileIdleHistory.dump(pw, nowELAPSED);
+            pw.println();
 
             if (mLastPriorityAlarmDispatch.size() > 0) {
                 pw.println("Last priority alarm dispatches:");
@@ -2702,6 +2762,23 @@
                 pw.decreaseIndent();
             }
 
+            if (mRemovalHistory.size() > 0) {
+                pw.println("Removal history: ");
+                pw.increaseIndent();
+                for (int i = 0; i < mRemovalHistory.size(); i++) {
+                    UserHandle.formatUid(pw, mRemovalHistory.keyAt(i));
+                    pw.println(":");
+                    pw.increaseIndent();
+                    final RemovedAlarm[] historyForUid = mRemovalHistory.valueAt(i).toArray();
+                    for (final RemovedAlarm removedAlarm : historyForUid) {
+                        removedAlarm.dump(pw, nowELAPSED, sdf);
+                    }
+                    pw.decreaseIndent();
+                }
+                pw.decreaseIndent();
+                pw.println();
+            }
+
             if (mLog.dump(pw, "Recent problems:")) {
                 pw.println();
             }
@@ -3239,7 +3316,7 @@
             }
             return !hasScheduleExactAlarmInternal(a.packageName, a.uid);
         };
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
     }
 
     /**
@@ -3247,7 +3324,6 @@
      * that the app is no longer eligible to use.
      *
      * This is not expected to get called frequently.
-     * TODO (b/179541791): Add revocation history to dumpsys.
      */
     void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
         Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!");
@@ -3263,26 +3339,22 @@
             }
             return false;
         };
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
     }
 
-    private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms) {
+    private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) {
+        final long nowRtc = mInjector.getCurrentTimeMillis();
+        final long nowElapsed = mInjector.getElapsedRealtime();
+
         final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
-        final boolean didRemove = !removedAlarms.isEmpty();
-        if (didRemove) {
-            for (final Alarm removed : removedAlarms) {
-                decrementAlarmCount(removed.uid, 1);
-            }
-        }
+        final boolean removedFromStore = !removedAlarms.isEmpty();
 
         for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
             final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
             for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
                 final Alarm alarm = alarmsForUid.get(j);
                 if (whichAlarms.test(alarm)) {
-                    // Don't set didRemove, since this doesn't impact the scheduled alarms.
-                    alarmsForUid.remove(j);
-                    decrementAlarmCount(alarm.uid, 1);
+                    removedAlarms.add(alarmsForUid.remove(j));
                 }
             }
             if (alarmsForUid.size() == 0) {
@@ -3292,13 +3364,24 @@
         for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
             final Alarm a = mPendingNonWakeupAlarms.get(i);
             if (whichAlarms.test(a)) {
-                // Don't set didRemove, since this doesn't impact the scheduled alarms.
-                mPendingNonWakeupAlarms.remove(i);
-                decrementAlarmCount(a.uid, 1);
+                removedAlarms.add(mPendingNonWakeupAlarms.remove(i));
             }
         }
 
-        if (didRemove) {
+        for (final Alarm removed : removedAlarms) {
+            decrementAlarmCount(removed.uid, 1);
+            if (!RemovedAlarm.isLoggable(reason)) {
+                continue;
+            }
+            RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
+            if (bufferForUid == null) {
+                bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
+                mRemovalHistory.put(removed.uid, bufferForUid);
+            }
+            bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed));
+        }
+
+        if (removedFromStore) {
             boolean idleUntilUpdated = false;
             if (mPendingIdleUntil != null && whichAlarms.test(mPendingIdleUntil)) {
                 mPendingIdleUntil = null;
@@ -3320,7 +3403,7 @@
         }
     }
 
-    void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+    void removeLocked(PendingIntent operation, IAlarmListener directReceiver, int reason) {
         if (operation == null && directReceiver == null) {
             if (localLOGV) {
                 Slog.w(TAG, "requested remove() of null operation",
@@ -3328,15 +3411,15 @@
             }
             return;
         }
-        removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver));
+        removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver), reason);
     }
 
-    void removeLocked(final int uid) {
+    void removeLocked(final int uid, int reason) {
         if (uid == Process.SYSTEM_UID) {
             // If a force-stop occurs for a system-uid package, ignore it.
             return;
         }
-        removeAlarmsInternalLocked(a -> a.uid == uid);
+        removeAlarmsInternalLocked(a -> a.uid == uid, reason);
     }
 
     void removeLocked(final String packageName) {
@@ -3347,7 +3430,7 @@
             }
             return;
         }
-        removeAlarmsInternalLocked(a -> a.matches(packageName));
+        removeAlarmsInternalLocked(a -> a.matches(packageName), REMOVE_REASON_UNDEFINED);
     }
 
     // Only called for ephemeral apps
@@ -3358,7 +3441,7 @@
         }
         final Predicate<Alarm> whichAlarms = (a) -> (a.uid == uid
                 && mActivityManagerInternal.isAppStartModeDisabled(uid, a.packageName));
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
     }
 
     void removeUserLocked(int userHandle) {
@@ -3368,13 +3451,18 @@
         }
         final Predicate<Alarm> whichAlarms =
                 (Alarm a) -> UserHandle.getUserId(a.uid) == userHandle;
-        removeAlarmsInternalLocked(whichAlarms);
+        removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
 
         for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
             if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
                 mLastPriorityAlarmDispatch.removeAt(i);
             }
         }
+        for (int i = mRemovalHistory.size() - 1; i >= 0; i--) {
+            if (UserHandle.getUserId(mRemovalHistory.keyAt(i)) == userHandle) {
+                mRemovalHistory.removeAt(i);
+            }
+        }
     }
 
     void interactiveStateChangedLocked(boolean interactive) {
@@ -4018,7 +4106,7 @@
                 case REMOVE_FOR_CANCELED:
                     final PendingIntent operation = (PendingIntent) msg.obj;
                     synchronized (mLock) {
-                        removeLocked(operation, null);
+                        removeLocked(operation, null, REMOVE_REASON_PI_CANCELLED);
                     }
                     break;
 
@@ -4198,6 +4286,7 @@
                         return;
                     case Intent.ACTION_UID_REMOVED:
                         mLastPriorityAlarmDispatch.delete(uid);
+                        mRemovalHistory.delete(uid);
                         return;
                     case Intent.ACTION_PACKAGE_REMOVED:
                         if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -4227,7 +4316,7 @@
                             // package-removed and package-restarted case
                             mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
                             mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
-                            removeLocked(uid);
+                            removeLocked(uid, REMOVE_REASON_UNDEFINED);
                         } else {
                             // external-applications-unavailable case
                             removeLocked(pkg);
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
index 3d5321c..e347ebf 100644
--- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
@@ -19,7 +19,7 @@
 import android.media.MediaParceledListSlice;
 
 /** {@hide} */
-interface IMediaCommunicationServiceCallback {
+oneway interface IMediaCommunicationServiceCallback {
     void onSession2Created(in Session2Token token);
     void onSession2Changed(in MediaParceledListSlice tokens);
 }
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 06de3f8..ed31aa3 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -65,17 +65,17 @@
 
     final Context mContext;
 
-    private final Object mLock = new Object();
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    final Object mLock = new Object();
+    final Handler mHandler = new Handler(Looper.getMainLooper());
 
     @GuardedBy("mLock")
     private final SparseIntArray mFullUserIds = new SparseIntArray();
     @GuardedBy("mLock")
     private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
 
-    private final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
+    final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
     @GuardedBy("mLock")
-    private final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
+    final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
     final NotificationManager mNotificationManager;
 
     public MediaCommunicationService(Context context) {
@@ -111,14 +111,14 @@
             FullUserRecord user = getFullUserRecordLocked(userId);
             if (user != null) {
                 if (user.getFullUserId() == userId) {
-                    user.destroySessionsForUserLocked(UserHandle.ALL.getIdentifier());
+                    user.destroyAllSessions();
                     mUserRecords.remove(userId);
                 } else {
-                    user.destroySessionsForUserLocked(userId);
+                    user.destroySessionsForUser(userId);
                 }
             }
-            updateUser();
         }
+        updateUser();
     }
 
     @Nullable
@@ -134,6 +134,22 @@
         return null;
     }
 
+    List<Session2Token> getSession2TokensLocked(int userId) {
+        List<Session2Token> list = new ArrayList<>();
+        if (userId == ALL.getIdentifier()) {
+            int size = mUserRecords.size();
+            for (int i = 0; i < size; i++) {
+                list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
+            }
+        } else {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user != null) {
+                list.addAll(user.getSession2Tokens(userId));
+            }
+        }
+        return list;
+    }
+
     private FullUserRecord getFullUserRecordLocked(int userId) {
         int fullUserId = mFullUserIds.get(userId, -1);
         if (fullUserId < 0) {
@@ -188,27 +204,54 @@
         }
     }
 
-    void dispatchSessionCreated(Session2Token token) {
-        for (CallbackRecord record : mCallbackRecords) {
-            if (record.mUserId != ALL.getIdentifier()
-                    && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
-                continue;
-            }
-            try {
-                record.mCallback.onSession2Created(token);
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    void onSessionDied(Session2Record record) {
+    void dispatchSession2Created(Session2Token token) {
         synchronized (mLock) {
-            destroySessionLocked(record);
+            for (CallbackRecord record : mCallbackRecords) {
+                if (record.mUserId != ALL.getIdentifier()
+                        && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
+                    continue;
+                }
+                try {
+                    record.mCallback.onSession2Created(token);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to notify session2 token created " + record);
+                }
+            }
         }
     }
 
-    private void destroySessionLocked(Session2Record session) {
+    void dispatchSession2Changed(int userId) {
+        MediaParceledListSlice<Session2Token> allSession2Tokens;
+        MediaParceledListSlice<Session2Token> userSession2Tokens;
+
+        synchronized (mLock) {
+            allSession2Tokens =
+                    new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
+            userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
+        }
+        allSession2Tokens.setInlineCountLimit(1);
+        userSession2Tokens.setInlineCountLimit(1);
+
+        synchronized (mLock) {
+            for (CallbackRecord record : mCallbackRecords) {
+                if (record.mUserId == ALL.getIdentifier()) {
+                    try {
+                        record.mCallback.onSession2Changed(allSession2Tokens);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+                    }
+                } else if (record.mUserId == userId) {
+                    try {
+                        record.mCallback.onSession2Changed(userSession2Tokens);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+                    }
+                }
+            }
+        }
+    }
+
+    void onSessionDied(Session2Record session) {
         if (DEBUG) {
             Log.d(TAG, "Destroying " + session);
         }
@@ -217,12 +260,10 @@
             return;
         }
 
-        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
-
+        FullUserRecord user = session.getFullUser();
         if (user != null) {
             user.removeSession(session);
         }
-
         session.close();
     }
 
@@ -241,17 +282,17 @@
                     throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
                             + " but actually=" + sessionToken.getUid());
                 }
+                FullUserRecord user;
+                int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
                 synchronized (mLock) {
-                    int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null) {
-                        Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
-                        return;
-                    }
-                    user.addSession(new Session2Record(MediaCommunicationService.this,
-                            sessionToken, mRecordExecutor));
-                    mHandler.post(() -> dispatchSessionCreated(sessionToken));
+                    user = getFullUserRecordLocked(userId);
                 }
+                if (user == null) {
+                    Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
+                    return;
+                }
+                user.addSession(new Session2Record(MediaCommunicationService.this,
+                        user, sessionToken, mRecordExecutor));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -299,10 +340,11 @@
                 int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
                 List<Session2Token> result;
                 synchronized (mLock) {
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    result = user.getSession2Tokens(resolvedUserId);
+                    result = getSession2TokensLocked(resolvedUserId);
                 }
-                return new MediaParceledListSlice(result);
+                MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
+                parceledListSlice.setInlineCountLimit(1);
+                return parceledListSlice;
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -427,7 +469,8 @@
 
     final class FullUserRecord {
         private final int mFullUserId;
-        /** Sorted list of media sessions */
+        private final Object mUserLock = new Object();
+        @GuardedBy("mUserLock")
         private final List<Session2Record> mSessionRecords = new ArrayList<>();
 
         FullUserRecord(int fullUserId) {
@@ -435,11 +478,18 @@
         }
 
         public void addSession(Session2Record record) {
-            mSessionRecords.add(record);
+            synchronized (mUserLock) {
+                mSessionRecords.add(record);
+            }
+            mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
+            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
         }
 
-        public void removeSession(Session2Record record) {
-            mSessionRecords.remove(record);
+        private void removeSession(Session2Record record) {
+            synchronized (mUserLock) {
+                mSessionRecords.remove(record);
+            }
+            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
             //TODO: Handle if the removed session was the media button session.
         }
 
@@ -447,42 +497,68 @@
             return mFullUserId;
         }
 
-        public List<Session2Token> getSession2Tokens(int userId) {
-            return mSessionRecords.stream()
-                    .filter(record -> record.isActive()
-                            && (userId == UserHandle.ALL.getIdentifier()
-                                    || record.getUserId() == userId))
-                    .map(Session2Record::getSessionToken)
-                    .collect(Collectors.toList());
+        public List<Session2Token> getAllSession2Tokens() {
+            synchronized (mUserLock) {
+                return mSessionRecords.stream()
+                        .map(Session2Record::getSessionToken)
+                        .collect(Collectors.toList());
+            }
         }
 
-        public void destroySessionsForUserLocked(int userId) {
-            synchronized (mLock) {
-                for (Session2Record record : mSessionRecords) {
-                    if (userId == UserHandle.ALL.getIdentifier()
-                            || record.getUserId() == userId) {
-                        destroySessionLocked(record);
+        public List<Session2Token> getSession2Tokens(int userId) {
+            synchronized (mUserLock) {
+                return mSessionRecords.stream()
+                        .filter(record -> record.getUserId() == userId)
+                        .map(Session2Record::getSessionToken)
+                        .collect(Collectors.toList());
+            }
+        }
+
+        public void destroyAllSessions() {
+            synchronized (mUserLock) {
+                for (Session2Record session : mSessionRecords) {
+                    session.close();
+                }
+                mSessionRecords.clear();
+            }
+            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+        }
+
+        public void destroySessionsForUser(int userId) {
+            boolean changed = false;
+            synchronized (mUserLock) {
+                for (int i = mSessionRecords.size() - 1; i >= 0; i--) {
+                    Session2Record session = mSessionRecords.get(i);
+                    if (session.getUserId() == userId) {
+                        mSessionRecords.remove(i);
+                        session.close();
+                        changed = true;
                     }
                 }
             }
+            if (changed) {
+                mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+            }
         }
     }
 
     static final class Session2Record {
-        private final Session2Token mSessionToken;
-        private final Object mLock = new Object();
-        private final WeakReference<MediaCommunicationService> mServiceRef;
-        @GuardedBy("mLock")
+        final Session2Token mSessionToken;
+        final Object mSession2RecordLock = new Object();
+        final WeakReference<MediaCommunicationService> mServiceRef;
+        final WeakReference<FullUserRecord> mFullUserRef;
+        @GuardedBy("mSession2RecordLock")
         private final MediaController2 mController;
 
-        @GuardedBy("mLock")
-        private boolean mIsConnected;
-        @GuardedBy("mLock")
+        @GuardedBy("mSession2RecordLock")
+        boolean mIsConnected;
+        @GuardedBy("mSession2RecordLock")
         private boolean mIsClosed;
 
-        Session2Record(MediaCommunicationService service, Session2Token token,
-                Executor controllerExecutor) {
+        Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
+                Session2Token token, Executor controllerExecutor) {
             mServiceRef = new WeakReference<>(service);
+            mFullUserRef = new WeakReference<>(fullUser);
             mSessionToken = token;
             mController = new MediaController2.Builder(service.getContext(), token)
                     .setControllerCallback(controllerExecutor, new Controller2Callback())
@@ -493,23 +569,19 @@
             return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
         }
 
-        public boolean isActive() {
-            synchronized (mLock) {
-                return mIsConnected;
-            }
+        public FullUserRecord getFullUser() {
+            return mFullUserRef.get();
         }
 
         public boolean isClosed() {
-            synchronized (mLock) {
+            synchronized (mSession2RecordLock) {
                 return mIsClosed;
             }
         }
 
         public void close() {
-            synchronized (mLock) {
+            synchronized (mSession2RecordLock) {
                 mIsClosed = true;
-                // Call close regardless of the mIsConnected. This may be called when it's not yet
-                // connected.
                 mController.close();
             }
         }
@@ -525,13 +597,9 @@
                 if (DEBUG) {
                     Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
                 }
-                synchronized (mLock) {
+                synchronized (mSession2RecordLock) {
                     mIsConnected = true;
                 }
-                MediaCommunicationService service = mServiceRef.get();
-                if (service != null) {
-                    //TODO: notify session state changed
-                }
             }
 
             @Override
@@ -539,7 +607,7 @@
                 if (DEBUG) {
                     Log.d(TAG, "disconnected from " + mSessionToken);
                 }
-                synchronized (mLock) {
+                synchronized (mSession2RecordLock) {
                     mIsConnected = false;
                 }
                 MediaCommunicationService service = mServiceRef.get();
diff --git a/core/api/current.txt b/core/api/current.txt
index 239ba65..2bada80 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -27110,7 +27110,7 @@
     method @NonNull public int[] getExposedCapabilities();
     method @NonNull public String getGatewayConnectionName();
     method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
-    method @NonNull public long[] getRetryIntervalsMs();
+    method @NonNull public long[] getRetryIntervalsMillis();
   }
 
   public static final class VcnGatewayConnectionConfig.Builder {
@@ -27119,7 +27119,7 @@
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
-    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMs(@NonNull long[]);
+    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
   }
 
   public class VcnManager {
@@ -41990,7 +41990,6 @@
     method @IntRange(from=1, to=261) public int getBand();
     method @IntRange(from=1) public int getCellBandwidthDownlinkKhz();
     method @IntRange(from=1) public int getCellBandwidthUplinkKhz();
-    method @Deprecated public int getChannelNumber();
     method public int getConnectionStatus();
     method @IntRange(from=0) public int getDownlinkChannelNumber();
     method @IntRange(from=0) public int getDownlinkFrequencyKhz();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 677da39..e48a1da 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -223,9 +223,9 @@
   public final class UnderlyingNetworkInfo implements android.os.Parcelable {
     ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
-    method @NonNull public String getIface();
+    method @NonNull public String getInterface();
     method public int getOwnerUid();
-    method @NonNull public java.util.List<java.lang.String> getUnderlyingIfaces();
+    method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 22af3f7..3537d8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -317,6 +317,7 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+    field public static final int durationBetweenRequestsMillis;
     field public static final int hotwordDetectionService;
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -325,7 +326,6 @@
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
     field public static final int sdkVersion = 16844304; // 0x1010610
     field public static final int supportsAmbientMode = 16844173; // 0x101058d
-    field public static final int throttleDurationMillis;
     field public static final int userRestriction = 16844164; // 0x1010584
   }
 
@@ -1448,11 +1448,12 @@
 package android.app.search {
 
   public final class Query implements android.os.Parcelable {
-    ctor public Query(@NonNull String, long, @Nullable android.os.Bundle);
+    ctor public Query(@NonNull String, long, @NonNull android.os.Bundle);
+    ctor public Query(@NonNull String, long);
     method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public String getInput();
-    method @NonNull public long getTimestamp();
+    method public long getTimestampMillis();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.search.Query> CREATOR;
   }
@@ -1485,9 +1486,10 @@
   }
 
   public final class SearchContext implements android.os.Parcelable {
-    ctor public SearchContext(int, int, @Nullable android.os.Bundle);
+    ctor public SearchContext(int, int);
+    ctor public SearchContext(int, int, @NonNull android.os.Bundle);
     method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @Nullable public String getPackageName();
     method @NonNull public int getResultTypes();
     method @NonNull public int getTimeoutMillis();
@@ -1497,7 +1499,6 @@
 
   public final class SearchSession implements java.lang.AutoCloseable {
     method public void close();
-    method public void destroy();
     method protected void finalize();
     method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
     method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
@@ -1512,7 +1513,7 @@
   public final class SearchTarget implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public String getId();
     method @NonNull public String getLayoutType();
     method @NonNull public String getPackageName();
@@ -1526,13 +1527,17 @@
     method public boolean shouldHide();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.search.SearchTarget> CREATOR;
+    field public static final int RESULT_TYPE_APPLICATION = 1; // 0x1
+    field public static final int RESULT_TYPE_SHORTCUT = 2; // 0x2
+    field public static final int RESULT_TYPE_SLICE = 4; // 0x4
+    field public static final int RESULT_TYPE_WIDGETS = 8; // 0x8
   }
 
   public static final class SearchTarget.Builder {
     ctor public SearchTarget.Builder(int, @NonNull String, @NonNull String);
     method @NonNull public android.app.search.SearchTarget build();
     method @NonNull public android.app.search.SearchTarget.Builder setAppWidgetProviderInfo(@NonNull android.appwidget.AppWidgetProviderInfo);
-    method @NonNull public android.app.search.SearchTarget.Builder setExtras(@Nullable android.os.Bundle);
+    method @NonNull public android.app.search.SearchTarget.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.app.search.SearchTarget.Builder setPackageName(@NonNull String);
     method @NonNull public android.app.search.SearchTarget.Builder setParentId(@NonNull String);
     method @NonNull public android.app.search.SearchTarget.Builder setScore(float);
@@ -9867,7 +9872,7 @@
   public final class DisplayHashParams implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.util.Size getBufferSize();
-    method public boolean isUseGrayscale();
+    method public boolean isGrayscaleBuffer();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
   }
@@ -9876,7 +9881,7 @@
     ctor public DisplayHashParams.Builder();
     method @NonNull public android.service.displayhash.DisplayHashParams build();
     method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
-    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean);
+    method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
   }
 
   public abstract class DisplayHashingService extends android.app.Service {
@@ -10260,10 +10265,10 @@
   public abstract class SearchUiService extends android.app.Service {
     ctor public SearchUiService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
     method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId);
     method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
     method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
+    method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
   }
 
 }
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index f366a54..65a8780 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,18 @@
 
 }
 
+package android.app.search {
+
+  public final class Query implements android.os.Parcelable {
+    method @Deprecated @NonNull public long getTimestamp();
+  }
+
+  public final class SearchSession implements java.lang.AutoCloseable {
+    method @Deprecated public void destroy();
+  }
+
+}
+
 package android.bluetooth {
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -163,6 +175,14 @@
 
 }
 
+package android.service.search {
+
+  public abstract class SearchUiService extends android.app.Service {
+    method @Deprecated public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
+  }
+
+}
+
 package android.telecom {
 
   public class TelecomManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a24555f..6d2d023 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1947,8 +1947,9 @@
             pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
             pw.print(" activityType="); pw.print(activityTypeToString(getActivityType()));
             pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode()));
-            pw.print(" supportsSplitScreenMultiWindow=");
-            pw.println(supportsSplitScreenMultiWindow);
+            pw.print(" supportsSplitScreenMultiWindow="); pw.print(supportsSplitScreenMultiWindow);
+            pw.print(" supportsMultiWindow=");
+            pw.println(supportsMultiWindow);
             if (taskDescription != null) {
                 pw.print("   ");
                 final ActivityManager.TaskDescription td = taskDescription;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6df9f4d..4a7fcd2 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -349,20 +349,6 @@
     }
 
     /**
-     * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
-     * if the request orientation is not met, and will be shown in size-compat mode if the container
-     * size has changed.
-     * @hide
-     */
-    public static boolean supportsNonResizableMultiWindow() {
-        try {
-            return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
      * @hide
      */
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 656942d..9ce37e4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3273,6 +3273,13 @@
                     dir = null;
                 }
             }
+            if (dir != null && !dir.canWrite()) {
+                // Older versions of the MediaProvider mainline module had a rare early boot race
+                // condition where app-private dirs could be created with the wrong permissions;
+                // fix this up here. This check should be very fast, because dir.exists() above
+                // will already have loaded the dentry in the cache.
+                sm.fixupAppDir(dir);
+            }
             result[i] = dir;
         }
         return result;
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b75e89c..74d51a0 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -259,13 +259,6 @@
     void setSplitScreenResizing(boolean resizing);
     boolean supportsLocalVoiceInteraction();
 
-    /**
-     * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
-     * if the request orientation is not met, and will be shown in size-compat mode if the container
-     * size has changed.
-     */
-    boolean supportsNonResizableMultiWindow();
-
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 6ad5eea..b95412f 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -138,6 +138,13 @@
     public boolean supportsSplitScreenMultiWindow;
 
     /**
+     * Whether this task supports multi windowing modes based on the device settings and the
+     * root activity resizability and configuration.
+     * @hide
+     */
+    public boolean supportsMultiWindow;
+
+    /**
      * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
      * @hide
      */
@@ -329,6 +336,7 @@
         }
         return topActivityType == that.topActivityType
                 && isResizeable == that.isResizeable
+                && supportsMultiWindow == that.supportsMultiWindow
                 && Objects.equals(positionInParent, that.positionInParent)
                 && Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
                 && getWindowingMode() == that.getWindowingMode()
@@ -375,6 +383,7 @@
 
         taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
         supportsSplitScreenMultiWindow = source.readBoolean();
+        supportsMultiWindow = source.readBoolean();
         resizeMode = source.readInt();
         configuration.readFromParcel(source);
         token = WindowContainerToken.CREATOR.createFromParcel(source);
@@ -412,6 +421,7 @@
 
         dest.writeTypedObject(taskDescription, flags);
         dest.writeBoolean(supportsSplitScreenMultiWindow);
+        dest.writeBoolean(supportsMultiWindow);
         dest.writeInt(resizeMode);
         configuration.writeToParcel(dest, flags);
         token.writeToParcel(dest, flags);
@@ -440,6 +450,7 @@
                 + " numActivities=" + numActivities
                 + " lastActiveTime=" + lastActiveTime
                 + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+                + " supportsMultiWindow=" + supportsMultiWindow
                 + " resizeMode=" + resizeMode
                 + " isResizeable=" + isResizeable
                 + " token=" + token
diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java
index 3ab20bb..34ace48 100644
--- a/core/java/android/app/search/Query.java
+++ b/core/java/android/app/search/Query.java
@@ -16,63 +16,108 @@
 package android.app.search;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
+ * Query object is sent over from client to the service.
+ *
+ * Inside the query object, there is a timestamp that trackes when the query string was typed.
+ *
+ * If this object was created for the {@link SearchSession#query},
+ * the client expects first consumer to be returned
+ * within {@link #getTimestampMillis()} + {@link SearchContext#getTimeoutMillis()}
+ * Base of the timestamp should be SystemClock.elasedRealTime()
+ *
  * @hide
  */
 @SystemApi
 public final class Query implements Parcelable {
 
     /**
-     * Query string typed from the client.
+     * string typed from the client.
      */
     @NonNull
     private final String mInput;
 
-    /**
-     * The timestamp that the query string was typed. If this object was created for the
-     * {@link SearchSession#query}, the client expects first consumer to be returned
-     * within mTimestamp + {@link SearchContext#mTimeoutMillis}
-     */
-    private final long mTimestamp;
+    private final long mTimestampMillis;
 
-    @Nullable
+    /**
+     * Contains other client UI constraints related data
+     */
+    @NonNull
     private final Bundle mExtras;
 
+    /**
+     * Query object used to pass search box input from client to service.
+     *
+     * @param input string typed from the client
+     * @param timestampMillis timestamp that query string was typed.
+     * @param extras bundle that contains other client UI constraints data
+     */
     public Query(@NonNull String input,
-            long timestamp,
-            @SuppressLint("NullableCollection")
-            @Nullable Bundle extras) {
+            long timestampMillis,
+            @NonNull Bundle extras) {
         mInput = input;
-        mTimestamp = timestamp;
-        mExtras = extras;
+        mTimestampMillis = timestampMillis;
+        mExtras = extras == null ? extras : new Bundle();
+    }
+
+    /**
+     * Query object used to pass search box input from client to service.
+     *
+     * @param input string typed from the client
+     * @param timestampMillis timestamp that query string was typed
+     */
+    public Query(@NonNull String input, long timestampMillis) {
+        this(input, timestampMillis, new Bundle());
     }
 
     private Query(Parcel parcel) {
         mInput = parcel.readString();
-        mTimestamp = parcel.readLong();
+        mTimestampMillis = parcel.readLong();
         mExtras = parcel.readBundle();
     }
 
+    /**
+     * @return string typed from the client
+     */
     @NonNull
     public String getInput() {
         return mInput;
     }
 
+    /**
+     * @deprecated Will be replaced by {@link #getTimestampMillis()} as soon as
+     * new SDK is adopted.
+     *
+     * @removed
+     */
+    @Deprecated
     @NonNull
     public long getTimestamp() {
-        return mTimestamp;
+        return mTimestampMillis;
     }
 
-    @Nullable
-    @SuppressLint("NullableCollection")
+    /**
+     * Base of the timestamp should be SystemClock.elasedRealTime()
+     *
+     * @return timestamp that query string was typed
+     */
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+    /**
+     * @return bundle that contains other client constraints related to the query
+     */
+    @NonNull
     public Bundle getExtras() {
+        if (mExtras == null) {
+            return new Bundle();
+        }
         return mExtras;
     }
 
@@ -84,7 +129,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mInput);
-        dest.writeLong(mTimestamp);
+        dest.writeLong(mTimestampMillis);
         dest.writeBundle(mExtras);
     }
 
diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java
index 548b7da..3e345fa 100644
--- a/core/java/android/app/search/SearchContext.java
+++ b/core/java/android/app/search/SearchContext.java
@@ -17,13 +17,20 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
 /**
+ * When {@link SearchSession} is created, {@link SearchContext} object is created
+ * to pass the result types from the {@link SearchSession#query(Query, Executor, Consumer)}
+ * method that the client wants.
+ *
  * @hide
  */
 @SystemApi
@@ -51,12 +58,25 @@
     @Nullable
     private String mPackageName;
 
+    /**
+     * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+     * @param timeoutMillis timeout before client renders its own fallback result
+     */
+    public SearchContext(int resultTypes, int timeoutMillis) {
+        this(resultTypes, timeoutMillis, new Bundle());
+    }
+
+    /**
+     * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+     * @param timeoutMillis timeout before client renders its own fallback result
+     * @param extras other client constraints (e.g., height of the search surface)
+     */
     public SearchContext(int resultTypes,
-            int queryTimeoutMillis,
-            @SuppressLint("NullableCollection") @Nullable Bundle extras) {
+            int timeoutMillis,
+            @NonNull Bundle extras) {
         mResultTypes = resultTypes;
-        mTimeoutMillis = queryTimeoutMillis;
-        mExtras = extras;
+        mTimeoutMillis = timeoutMillis;
+        mExtras = Objects.requireNonNull(extras);
     }
 
     private SearchContext(Parcel parcel) {
@@ -74,7 +94,7 @@
     /**
      * @hide
      */
-    public void setPackageName(@Nullable String packageName) {
+    void setPackageName(@Nullable String packageName) {
         mPackageName = packageName;
     }
 
@@ -83,8 +103,7 @@
         return mTimeoutMillis;
     }
 
-    @Nullable
-    @SuppressLint("NullableCollection")
+    @NonNull
     public Bundle getExtras() {
         return mExtras;
     }
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 7bd88d9..a5425a2 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -37,7 +37,9 @@
 import java.util.function.Consumer;
 
 /**
- * Client API to share information about the search UI state and execute query.
+ * Client needs to create {@link SearchSession} object from in order to execute
+ * {@link #query(Query, Executor, Consumer)} method and share client side signals
+ * back to the service using {@link #notifyEvent(Query, SearchTargetEvent)}.
  *
  * <p>
  * Usage: <pre> {@code
@@ -60,7 +62,7 @@
  *    }
  *
  *    void onDestroy() {
- *        mSearchSession.destroy();
+ *        mSearchSession.close();
  *    }
  *
  * }</pre>
@@ -108,7 +110,10 @@
     }
 
     /**
-     * Notifies the search service of an search target event.
+     * Notifies the search service of an search target event (e.g., user interaction
+     * and lifecycle event of the search surface).
+     *
+     * {@see SearchTargetEvent}
      *
      * @param query input object associated with the event.
      * @param event The {@link SearchTargetEvent} that represents the search target event.
@@ -153,7 +158,11 @@
     /**
      * Destroys the client and unregisters the callback. Any method on this class after this call
      * will throw {@link IllegalStateException}.
+     *
+     * @deprecated
+     * @removed
      */
+    @Deprecated
     public void destroy() {
         if (!mIsClosed.getAndSet(true)) {
             mCloseGuard.close();
@@ -188,6 +197,11 @@
         }
     }
 
+    /**
+     * Destroys the client and unregisters the callback. Any method on this class after this call
+     * will throw {@link IllegalStateException}.
+     *
+     */
     @Override
     public void close() {
         try {
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index 6a80f8b..56c5ddf 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -15,34 +15,73 @@
  */
 package android.app.search;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.app.slice.SliceManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
- * A representation of a searchable item info.
+ * A representation of a search result. Search result can be expressed in one of the following:
+ * app icon, shortcut, slice, widget, or a custom object using {@link SearchAction}. While
+ * app icon ({@link PackageManager}, shortcut {@link ShortcutManager}, slice {@link SliceManager},
+ * or widget (@link AppWidgetManager} are published content backed by the system service,
+ * {@link SearchAction} is a custom object that the service can use to send search result to the
+ * client.
+ *
+ * These various types of Android primitives could be defined as {@link SearchResultType}. Some
+ * times, the result type can define the layout type that that this object can be rendered in.
+ * (e.g., app widget). Most times, {@link #getLayoutType()} assigned by the service
+ * can recommend which layout this target should be rendered in.
+ *
+ * The service can also use fields such as {@link #getScore()} to indicate
+ * how confidence the search result is and {@link #shouldHide()} to indicate
+ * whether it is recommended to be shown by default.
+ *
+ * Finally, {@link #getId()} is the unique identifier of this search target and a single
+ * search target is defined by being able to express a single launcheable item. In case the
+ * service want to recommend how to combine multiple search target objects to render in a group
+ * (e.g., same row), {@link #getParentId()} can be assigned on the sub targets of the group
+ * using the primary search target's identifier.
  *
  * @hide
  */
 @SystemApi
 public final class SearchTarget implements Parcelable {
 
-
-    @NonNull
+    public static final int RESULT_TYPE_APPLICATION = 1 << 0;
+    public static final int RESULT_TYPE_SHORTCUT = 1 << 1;
+    public static final int RESULT_TYPE_SLICE = 1 << 2;
+    public static final int RESULT_TYPE_WIDGETS = 1 << 3;
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"RESULT_TYPE_"}, value = {
+            RESULT_TYPE_APPLICATION,
+            RESULT_TYPE_SHORTCUT,
+            RESULT_TYPE_SLICE,
+            RESULT_TYPE_WIDGETS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SearchResultType {}
     private final int mResultType;
 
     /**
-     * Constant to express how the group of {@link SearchTarget} should be laid out.
+     * Constant to express how the group of {@link SearchTarget} should be rendered on
+     * the client side. (e.g., "icon", "icon_row", "short_icon_row")
      */
     @NonNull
     private final String mLayoutType;
@@ -69,13 +108,13 @@
     private final AppWidgetProviderInfo mAppWidgetProviderInfo;
     @Nullable
     private final Uri mSliceUri;
-    @Nullable
+
+    @NonNull
     private final Bundle mExtras;
 
     private SearchTarget(Parcel parcel) {
         mResultType = parcel.readInt();
         mLayoutType = parcel.readString();
-
         mId = parcel.readString();
         mParentId = parcel.readString();
         mScore = parcel.readFloat();
@@ -102,7 +141,7 @@
             @Nullable ShortcutInfo shortcutInfo,
             @Nullable Uri sliceUri,
             @Nullable AppWidgetProviderInfo appWidgetProviderInfo,
-            @Nullable Bundle extras) {
+            @NonNull Bundle extras) {
         mResultType = resultType;
         mLayoutType = Objects.requireNonNull(layoutType);
         mId = Objects.requireNonNull(id);
@@ -129,9 +168,9 @@
     }
 
     /**
-     * Retrieves the result type.
+     * Retrieves the result type {@see SearchResultType}.
      */
-    public int getResultType() {
+    public @SearchResultType int getResultType() {
         return mResultType;
     }
 
@@ -167,7 +206,7 @@
     }
 
     /**
-     * TODO: add comment
+     * Indicates whether this object should be hidden and shown only on demand.
      */
     public boolean shouldHide() {
         return mShouldHide;
@@ -198,7 +237,7 @@
     }
 
     /**
-     * Return widget provider info.
+     * Return a widget provider info.
      */
     @Nullable
     public AppWidgetProviderInfo getAppWidgetProviderInfo() {
@@ -206,7 +245,7 @@
     }
 
     /**
-     * Return slice uri.
+     * Returns a slice uri.
      */
     @Nullable
     public Uri getSliceUri() {
@@ -214,7 +253,7 @@
     }
 
     /**
-     * Return search action.
+     * Returns a search action.
      */
     @Nullable
     public SearchAction getSearchAction() {
@@ -224,8 +263,7 @@
     /**
      * Return extra bundle.
      */
-    @Nullable
-    @SuppressLint("NullableCollection")
+    @NonNull
     public Bundle getExtras() {
         return mExtras;
     }
@@ -295,10 +333,10 @@
         private Uri mSliceUri;
         @Nullable
         private AppWidgetProviderInfo mAppWidgetProviderInfo;
-        @Nullable
+        @NonNull
         private Bundle mExtras;
 
-        public Builder(int resultType,
+        public Builder(@SearchResultType int resultType,
                 @NonNull String layoutType,
                 @NonNull String id) {
             mId = id;
@@ -369,32 +407,30 @@
          */
         @NonNull
         public Builder setSliceUri(@NonNull Uri sliceUri) {
-            // TODO: add packageName check
             mSliceUri = sliceUri;
             return this;
         }
 
         /**
-         * TODO: add comment
+         * Set the {@link SearchAction} object to this target.
          */
         @NonNull
-        public Builder setSearchAction(@Nullable SearchAction remoteAction) {
-            // TODO: add packageName check
-            mSearchAction = remoteAction;
+        public Builder setSearchAction(@Nullable SearchAction searchAction) {
+            mSearchAction = searchAction;
             return this;
         }
 
         /**
-         * TODO: add comment
+         * Set any extra information that needs to be shared between service and the client.
          */
         @NonNull
-        public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
-            mExtras = extras;
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = Objects.requireNonNull(extras);
             return this;
         }
 
         /**
-         * TODO: add comment
+         * Sets the score of the object.
          */
         @NonNull
         public Builder setScore(float score) {
@@ -403,7 +439,7 @@
         }
 
         /**
-         * TODO: add comment
+         * Sets whether the result should be hidden by default inside client.
          */
         @NonNull
         public Builder setShouldHide(boolean shouldHide) {
diff --git a/core/java/android/app/search/SearchTargetEvent.java b/core/java/android/app/search/SearchTargetEvent.java
index f478dc3..d4915af 100644
--- a/core/java/android/app/search/SearchTargetEvent.java
+++ b/core/java/android/app/search/SearchTargetEvent.java
@@ -29,7 +29,11 @@
 import java.util.Objects;
 
 /**
- * A representation of an app target event.
+ * A representation of an search target event.
+ *
+ * There are two types of events. First type of event correspends to the user interaction
+ * that happens on the search surface. (e.g., {@link #ACTION_TAP}. Second type of events
+ * correspends to the lifecycle event of the search surface {@link #ACTION_SURFACE_VISIBLE}.
  *
  * @hide
  */
diff --git a/core/java/android/app/search/SearchUiManager.java b/core/java/android/app/search/SearchUiManager.java
index 636bfe1..ce6d8b2 100644
--- a/core/java/android/app/search/SearchUiManager.java
+++ b/core/java/android/app/search/SearchUiManager.java
@@ -25,6 +25,12 @@
 /**
  * Class that provides methods to create search ui session clients.
  *
+ * Usage: <pre> {@code
+ *    mSearchUiManager = context.getSystemService(SearchUiManager.class);
+ *    mSearchSession.createSearchSession(searchContext)
+ *
+ * }</pre>
+ *
  * @hide
  */
 @SystemApi
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 7e1df1b..114ad87 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1885,9 +1885,9 @@
      * in {@link android.provider.MediaStore.MediaColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file.  May be "r" for read-only access,
-     * "rw" for read and write access, or "rwt" for read and write access
-     * that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      *
      * @return Returns a new ParcelFileDescriptor which you can use to access
      * the file.
@@ -1948,10 +1948,9 @@
      * in {@link android.provider.MediaStore.MediaColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file. May be "r" for read-only access,
-     *            "w" for write-only access, "rw" for read and write access, or
-     *            "rwt" for read and write access that truncates any existing
-     *            file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param signal A signal to cancel the operation in progress, or
      *            {@code null} if none. For example, if you are downloading a
      *            file from the network to service a "rw" mode request, you
@@ -2011,11 +2010,9 @@
      * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file.  May be "r" for read-only access,
-     * "w" for write-only access (erasing whatever data is currently in
-     * the file), "wa" for write-only access to append to any existing data,
-     * "rw" for read and write access on any existing data, and "rwt" for read
-     * and write access that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      *
      * @return Returns a new AssetFileDescriptor which you can use to access
      * the file.
@@ -2068,11 +2065,9 @@
      * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
      *
      * @param uri The URI whose file is to be opened.
-     * @param mode Access mode for the file.  May be "r" for read-only access,
-     * "w" for write-only access (erasing whatever data is currently in
-     * the file), "wa" for write-only access to append to any existing data,
-     * "rw" for read and write access on any existing data, and "rwt" for read
-     * and write access that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param signal A signal to cancel the operation in progress, or
      *            {@code null} if none. For example, if you are downloading a
      *            file from the network to service a "rw" mode request, you
@@ -2103,11 +2098,9 @@
      * by looking up a column named "_data" at the given URI.
      *
      * @param uri The URI to be opened.
-     * @param mode The file mode.  May be "r" for read-only access,
-     * "w" for write-only access (erasing whatever data is currently in
-     * the file), "wa" for write-only access to append to any existing data,
-     * "rw" for read and write access on any existing data, and "rwt" for read
-     * and write access that truncates any existing file.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      *
      * @return Returns a new ParcelFileDescriptor that can be used by the
      * client to access the file.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index aec39da..1132991 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,9 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.system.ErrnoException;
 import android.system.Int64Ref;
+import android.system.Os;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -76,8 +78,10 @@
 import dalvik.system.CloseGuard;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -864,6 +868,20 @@
         return wrap((ContentInterface) wrapped);
     }
 
+    /**
+     * Offer to locally truncate the given file when opened using the write-only
+     * mode. This is typically used to preserve legacy compatibility behavior.
+     */
+    private static void maybeTruncate(FileDescriptor fd, String mode) throws FileNotFoundException {
+        if ("w".equals(mode)) {
+            try {
+                Os.ftruncate(fd, 0);
+            } catch (ErrnoException e) {
+                throw new FileNotFoundException("Failed to truncate: " + e.getMessage());
+            }
+        }
+    }
+
     /** @hide */
     @SuppressWarnings("HiddenAbstractMethod")
     @UnsupportedAppUsage
@@ -1525,8 +1543,20 @@
     }
 
     /**
-     * Synonym for {@link #openOutputStream(Uri, String)
-     * openOutputStream(uri, "w")}.
+     * Open a stream on to the content associated with a content URI.  If there
+     * is no data associated with the URI, FileNotFoundException is thrown.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link #SCHEME_CONTENT})</li>
+     * <li>file ({@link #SCHEME_FILE})</li>
+     * </ul>
+     *
+     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+     * on these schemes.
+     *
+     * <p>This method behaves like {@link FileOutputStream} and automatically
+     * truncates any existing contents.
      *
      * @param uri The desired URI.
      * @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1534,7 +1564,16 @@
      */
     public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
             throws FileNotFoundException {
-        return openOutputStream(uri, "w");
+        AssetFileDescriptor fd = openAssetFileDescriptor(uri, "w", null);
+        if (fd == null) return null;
+        try {
+            final FileOutputStream res = fd.createOutputStream();
+            // Unconditionally truncate to mirror FileOutputStream behavior
+            maybeTruncate(res.getFD(), "w");
+            return res;
+        } catch (IOException e) {
+            throw new FileNotFoundException("Unable to create stream");
+        }
     }
 
     /**
@@ -1551,7 +1590,9 @@
      * on these schemes.
      *
      * @param uri The desired URI.
-     * @param mode May be "w", "wa", "rw", or "rwt".
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @return an OutputStream or {@code null} if the provider recently crashed.
      * @throws FileNotFoundException if the provided URI could not be opened.
      * @see #openAssetFileDescriptor(Uri, String)
@@ -1559,8 +1600,14 @@
     public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
             throws FileNotFoundException {
         AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
+        if (fd == null) return null;
         try {
-            return fd != null ? fd.createOutputStream() : null;
+            final FileOutputStream res = fd.createOutputStream();
+            // Preserve legacy behavior by offering to truncate
+            if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+                maybeTruncate(res.getFD(), mode);
+            }
+            return res;
         } catch (IOException e) {
             throw new FileNotFoundException("Unable to create stream");
         }
@@ -1607,8 +1654,9 @@
      * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openFile
-     * ContentProvider.openFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
      * provider recently crashed. You own this descriptor and are responsible for closing it
      * when done.
@@ -1650,8 +1698,9 @@
      * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openFile
-     * ContentProvider.openFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param cancellationSignal A signal to cancel the operation in progress,
      *         or null if none. If the operation is canceled, then
      *         {@link OperationCanceledException} will be thrown.
@@ -1744,8 +1793,9 @@
      * from any built-in data conversion that a provider implements.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
-     * ContentProvider.openAssetFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
      * provider recently crashed. You own this descriptor and are responsible for closing it
      * when done.
@@ -1798,8 +1848,9 @@
      * from any built-in data conversion that a provider implements.
      *
      * @param uri The desired URI to open.
-     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
-     * ContentProvider.openAssetFile}.
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt". See
+     *            {@link ParcelFileDescriptor#parseMode} for more details.
      * @param cancellationSignal A signal to cancel the operation in progress, or null if
      *            none. If the operation is canceled, then
      *            {@link OperationCanceledException} will be thrown.
@@ -1835,6 +1886,10 @@
         } else if (SCHEME_FILE.equals(scheme)) {
             ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
                     new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
+            // Preserve legacy behavior by offering to truncate
+            if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+                maybeTruncate(pfd.getFileDescriptor(), mode);
+            }
             return new AssetFileDescriptor(pfd, 0, -1);
         } else {
             if ("r".equals(mode)) {
@@ -1892,6 +1947,11 @@
                     // ParcelFileDescriptorInner do that when it is closed.
                     stableProvider = null;
 
+                    // Preserve legacy behavior by offering to truncate
+                    if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+                        maybeTruncate(pfd.getFileDescriptor(), mode);
+                    }
+
                     return new AssetFileDescriptor(pfd, fd.getStartOffset(),
                             fd.getDeclaredLength());
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b498325..7c7cfdb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6397,7 +6397,9 @@
     public static final int FLAG_ACTIVITY_NO_HISTORY = 0x40000000;
     /**
      * If set, the activity will not be launched if it is already running
-     * at the top of the history stack.
+     * at the top of the history stack.  See
+     * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html#TaskLaunchModes">
+     * Tasks and Back Stack</a> for more information.
      */
     public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
     /**
@@ -6537,8 +6539,7 @@
     public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
     /**
      * This flag is not normally set by application code, but set for you by
-     * the system if this activity is being launched from history
-     * (longpress home key).
+     * the system if this activity is being launched from history.
      */
     public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
     /**
@@ -6571,7 +6572,9 @@
      * equivalent of the Activity manifest specifying {@link
      * android.R.attr#documentLaunchMode}="intoExisting". When used with
      * FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
-     * {@link android.R.attr#documentLaunchMode}="always".
+     * {@link android.R.attr#documentLaunchMode}="always". The flag is ignored even in
+     * conjunction with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} when the Activity manifest specifies
+     * {@link android.R.attr#documentLaunchMode}="never".
      *
      * Refer to {@link android.R.attr#documentLaunchMode} for more information.
      *
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 48d3686..d7b96df 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2312,7 +2312,7 @@
         if (setVisible) {
             cancelImeSurfaceRemoval();
         }
-        mPrivOps.applyImeVisibility(setVisible
+        mPrivOps.applyImeVisibilityAsync(setVisible
                 ? mCurShowInputToken : mCurHideInputToken, setVisible);
     }
 
@@ -3319,7 +3319,7 @@
             if (mNotifyUserActionSent) {
                 return;
             }
-            mPrivOps.notifyUserAction();
+            mPrivOps.notifyUserActionAsync();
             mNotifyUserActionSent = true;
         }
     }
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 459fdac..33f9375 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -71,13 +71,13 @@
 
     /** Get the interface name of this network. */
     @NonNull
-    public String getIface() {
+    public String getInterface() {
         return mIface;
     }
 
     /** Get the names of the interfaces underlying this network. */
     @NonNull
-    public List<String> getUnderlyingIfaces() {
+    public List<String> getUnderlyingInterfaces() {
         return mUnderlyingIfaces;
     }
 
@@ -124,8 +124,8 @@
         if (!(o instanceof UnderlyingNetworkInfo)) return false;
         final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
         return mOwnerUid == that.getOwnerUid()
-                && Objects.equals(mIface, that.getIface())
-                && Objects.equals(mUnderlyingIfaces, that.getUnderlyingIfaces());
+                && Objects.equals(mIface, that.getInterface())
+                && Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
     }
 
     @Override
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index eedeeb5..f02346b 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -141,7 +141,7 @@
      * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
      * than this value.
      *
-     * @see {@link Builder#setRetryIntervalsMs()}
+     * @see {@link Builder#setRetryIntervalsMillis()}
      */
     private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
 
@@ -342,10 +342,10 @@
     /**
      * Retrieves the configured retry intervals.
      *
-     * @see Builder#setRetryIntervalsMs(long[])
+     * @see Builder#setRetryIntervalsMillis(long[])
      */
     @NonNull
-    public long[] getRetryIntervalsMs() {
+    public long[] getRetryIntervalsMillis() {
         return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
     }
 
@@ -555,7 +555,7 @@
          * @see VcnManager for additional discussion on fail-safe mode
          */
         @NonNull
-        public Builder setRetryIntervalsMs(@NonNull long[] retryIntervalsMs) {
+        public Builder setRetryIntervalsMillis(@NonNull long[] retryIntervalsMs) {
             validateRetryInterval(retryIntervalsMs);
 
             mRetryIntervalsMs = retryIntervalsMs;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c084787..0c9ad1b 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -31,6 +31,7 @@
 import static android.system.OsConstants.S_ISREG;
 import static android.system.OsConstants.S_IWOTH;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -63,6 +64,8 @@
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.io.UncheckedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.nio.ByteOrder;
@@ -110,6 +113,20 @@
 
     private final CloseGuard mGuard = CloseGuard.get();
 
+    /** @hide */
+    @IntDef(prefix = {"MODE_"}, value = {
+            MODE_WORLD_READABLE,
+            MODE_WORLD_WRITEABLE,
+            MODE_READ_ONLY,
+            MODE_WRITE_ONLY,
+            MODE_READ_WRITE,
+            MODE_CREATE,
+            MODE_TRUNCATE,
+            MODE_APPEND,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Mode { }
+
     /**
      * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
      * this file doesn't already exist, then create the file with permissions
@@ -227,7 +244,8 @@
      *             be opened with the requested mode.
      * @see #parseMode(String)
      */
-    public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
+    public static ParcelFileDescriptor open(File file, @Mode int mode)
+            throws FileNotFoundException {
         final FileDescriptor fd = openInternal(file, mode);
         if (fd == null) return null;
 
@@ -259,7 +277,7 @@
     // We can't accept a generic Executor here, since we need to use
     // MessageQueue.addOnFileDescriptorEventListener()
     @SuppressLint("ExecutorRegistration")
-    public static ParcelFileDescriptor open(File file, int mode, Handler handler,
+    public static ParcelFileDescriptor open(File file, @Mode int mode, Handler handler,
             final OnCloseListener listener) throws IOException {
         if (handler == null) {
             throw new IllegalArgumentException("Handler must not be null");
@@ -330,7 +348,8 @@
         return pfd;
     }
 
-    private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
+    private static FileDescriptor openInternal(File file, @Mode int mode)
+            throws FileNotFoundException {
         final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
 
         int realMode = S_IRWXU | S_IRWXG;
@@ -623,15 +642,36 @@
     }
 
     /**
-     * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
-     * with {@link #open}.
+     * Converts a string representing a file mode, such as "rw", into a bitmask
+     * suitable for use with {@link #open}.
      * <p>
-     * @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
-     *             or "rwt".
+     * The argument must define at least one of the following base access modes:
+     * <ul>
+     * <li>"r" indicates the file should be opened in read-only mode, equivalent
+     * to {@link OsConstants#O_RDONLY}.
+     * <li>"w" indicates the file should be opened in write-only mode,
+     * equivalent to {@link OsConstants#O_WRONLY}.
+     * <li>"rw" indicates the file should be opened in read-write mode,
+     * equivalent to {@link OsConstants#O_RDWR}.
+     * </ul>
+     * In addition to a base access mode, the following additional modes may
+     * requested:
+     * <ul>
+     * <li>"a" indicates the file should be opened in append mode, equivalent to
+     * {@link OsConstants#O_APPEND}. Before each write, the file offset is
+     * positioned at the end of the file.
+     * <li>"t" indicates the file should be opened in truncate mode, equivalent
+     * to {@link OsConstants#O_TRUNC}. If the file already exists and is a
+     * regular file and is opened for writing, it will be truncated to length 0.
+     * </ul>
+     *
+     * @param mode The string representation of the file mode. Can be "r", "w",
+     *            "wt", "wa", "rw" or "rwt".
      * @return A bitmask representing the given file mode.
-     * @throws IllegalArgumentException if the given string does not match a known file mode.
+     * @throws IllegalArgumentException if the given string does not match a
+     *             known file mode.
      */
-    public static int parseMode(String mode) {
+    public static @Mode int parseMode(String mode) {
         return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 98abd96..c8e5e42 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8515,6 +8515,12 @@
                 "swipe_bottom_to_notification_enabled";
 
         /**
+         * Controls whether One-Handed mode is currently activated.
+         * @hide
+         */
+        public static final String ONE_HANDED_MODE_ACTIVATED = "one_handed_mode_activated";
+
+        /**
          * For user preference if One-Handed Mode enabled.
          * @hide
          */
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
index 2ec9d5d..123c209 100644
--- a/core/java/android/service/displayhash/DisplayHashParams.java
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -48,7 +48,7 @@
     /**
      * Whether the content will be captured in grayscale or color.
      */
-    private final boolean mUseGrayscale;
+    private final boolean mGrayscaleBuffer;
 
     /**
      * A builder for {@link DisplayHashParams}
@@ -56,7 +56,7 @@
     public static final class Builder {
         @Nullable
         private Size mBufferSize;
-        private boolean mUseGrayscale;
+        private boolean mGrayscaleBuffer;
 
         /**
          * Creates a new Builder.
@@ -77,15 +77,15 @@
          * Whether the content will be captured in grayscale or color.
          */
         @NonNull
-        public Builder setUseGrayscale(boolean useGrayscale) {
-            mUseGrayscale = useGrayscale;
+        public Builder setGrayscaleBuffer(boolean grayscaleBuffer) {
+            mGrayscaleBuffer = grayscaleBuffer;
             return this;
         }
 
         /** Builds the instance. This builder should not be touched after calling this! */
         @NonNull
         public DisplayHashParams build() {
-            return new DisplayHashParams(mBufferSize, mUseGrayscale);
+            return new DisplayHashParams(mBufferSize, mGrayscaleBuffer);
         }
     }
 
@@ -112,16 +112,16 @@
      *   buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
      *   HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
      *   value set here. If {@code null}, the buffer size will not be changed.
-     * @param useGrayscale
+     * @param grayscaleBuffer
      *   Whether the content will be captured in grayscale or color.
      * @hide
      */
     @DataClass.Generated.Member
     public DisplayHashParams(
             @Nullable Size bufferSize,
-            boolean useGrayscale) {
+            boolean grayscaleBuffer) {
         this.mBufferSize = bufferSize;
-        this.mUseGrayscale = useGrayscale;
+        this.mGrayscaleBuffer = grayscaleBuffer;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -141,8 +141,8 @@
      * Whether the content will be captured in grayscale or color.
      */
     @DataClass.Generated.Member
-    public boolean isUseGrayscale() {
-        return mUseGrayscale;
+    public boolean isGrayscaleBuffer() {
+        return mGrayscaleBuffer;
     }
 
     @Override
@@ -153,7 +153,7 @@
 
         return "DisplayHashParams { " +
                 "bufferSize = " + mBufferSize + ", " +
-                "useGrayscale = " + mUseGrayscale +
+                "grayscaleBuffer = " + mGrayscaleBuffer +
         " }";
     }
 
@@ -164,7 +164,7 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mUseGrayscale) flg |= 0x2;
+        if (mGrayscaleBuffer) flg |= 0x2;
         if (mBufferSize != null) flg |= 0x1;
         dest.writeByte(flg);
         if (mBufferSize != null) dest.writeSize(mBufferSize);
@@ -182,11 +182,11 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
-        boolean useGrayscale = (flg & 0x2) != 0;
+        boolean grayscaleBuffer = (flg & 0x2) != 0;
         Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
 
         this.mBufferSize = bufferSize;
-        this.mUseGrayscale = useGrayscale;
+        this.mGrayscaleBuffer = grayscaleBuffer;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -206,10 +206,10 @@
     };
 
     @DataClass.Generated(
-            time = 1618436855096L,
+            time = 1619638159453L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
-            inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final  boolean mUseGrayscale\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate  boolean mUseGrayscale\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final  boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate  boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java
index d0e4195..c04dd9c 100644
--- a/core/java/android/service/search/SearchUiService.java
+++ b/core/java/android/service/search/SearchUiService.java
@@ -45,6 +45,9 @@
  * A service used to share the lifecycle of search UI (open, close, interaction)
  * and also to return search result on a query.
  *
+ * To understand the lifecycle of search session and how a query get issued,
+ * {@see SearchSession}
+ *
  * @hide
  */
 @SystemApi
@@ -71,6 +74,10 @@
         @Override
         public void onCreateSearchSession(SearchContext context, SearchSessionId sessionId) {
             mHandler.sendMessage(
+                    obtainMessage(SearchUiService::onSearchSessionCreated,
+                            SearchUiService.this, context, sessionId));
+            // to be removed
+            mHandler.sendMessage(
                     obtainMessage(SearchUiService::onCreateSearchSession,
                             SearchUiService.this, context, sessionId));
         }
@@ -119,11 +126,24 @@
 
     /**
      * Creates a new search session.
+     *
+     * @deprecated this is method will be removed as soon as
+     * {@link #onSearchSessionCreated(SearchContext, SearchSessionId)}
+     * is adopted by the service.
+     *
+     * @removed
      */
+    @Deprecated
     public void onCreateSearchSession(@NonNull SearchContext context,
             @NonNull SearchSessionId sessionId) {}
 
     /**
+     * A new search session is created.
+     */
+    public void onSearchSessionCreated(@NonNull SearchContext context,
+            @NonNull SearchSessionId sessionId) {}
+
+    /**
      * Called by the client to request search results using a query string.
      */
     @MainThread
@@ -132,7 +152,10 @@
             @NonNull Consumer<List<SearchTarget>> callback);
 
     /**
-     * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s).
+     * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s)
+     * and lifecycle event on the search surface (e.g., visibility change).
+     *
+     * {@see SearchTargetEvent}
      */
     @MainThread
     public abstract void onNotifyEvent(@NonNull SearchSessionId sessionId,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5a738eb..0958f3f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5191,6 +5191,17 @@
 
         @Override
         public void handleMessage(Message msg) {
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, getMessageName(msg));
+            }
+            try {
+                handleMessageImpl(msg);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            }
+        }
+
+        private void handleMessageImpl(Message msg) {
             switch (msg.what) {
                 case MSG_INVALIDATE:
                     ((View) msg.obj).invalidate();
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index b0d95b3..8dbce1b 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -254,6 +254,11 @@
     }
 
     /** @hide */
+    public TranslationContext getTranslationContext() {
+        return mTranslationContext;
+    }
+
+    /** @hide */
     public int getTranslatorId() {
         return mId;
     }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 0425572..bdf217d 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -451,13 +451,14 @@
             int[] supportedFormats = getSupportedFormatsLocked();
             ArrayList<ViewRootImpl> roots =
                     WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+            TranslationCapability capability =
+                    getTranslationCapability(translator.getTranslationContext());
             mActivity.runOnUiThread(() -> {
                 // traverse the hierarchy to collect ViewTranslationRequests
                 for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
                     View rootView = roots.get(rootNum).getView();
-                    // TODO(b/183589662): call getTranslationCapabilities() for capability
-                    rootView.dispatchRequestTranslation(viewIds, supportedFormats, /* capability */
-                            null, requests);
+                    rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
+                            requests);
                 }
                 mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
                         UiTranslationController::sendTranslationRequest,
@@ -487,6 +488,15 @@
         return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
     }
 
+    private TranslationCapability getTranslationCapability(TranslationContext translationContext) {
+        // We only support text to text capability now, we will query real status from service when
+        // we support more translation capabilities.
+        return new TranslationCapability(TranslationCapability.STATE_ON_DEVICE,
+                translationContext.getSourceSpec(),
+                translationContext.getTargetSpec(), /* uiTranslationEnabled= */ true,
+                /* supportedTranslationFlags= */ 0);
+    }
+
     private void findViewsTraversalByAutofillIds(IntArray sourceViewIds) {
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 166411e..e06d5f0 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -259,6 +259,20 @@
     // Used to highlight a word when it is corrected by the IME
     private CorrectionHighlighter mCorrectionHighlighter;
 
+    /**
+     * {@code true} when {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)}
+     * is being executed and {@link InputMethodManager#restartInput(View)} is scheduled to be
+     * called.
+     *
+     * <p>This is also used to avoid an unnecessary invocation of
+     * {@link InputMethodManager#updateSelection(View, int, int, int, int)} when
+     * {@link InputMethodManager#restartInput(View)} is scheduled to be called already
+     * See bug 186582769 for details.</p>
+     *
+     * <p>TODO(186582769): Come up with better way.</p>
+     */
+    private boolean mHasPendingRestartInputForSetText = false;
+
     InputContentType mInputContentType;
     InputMethodState mInputMethodState;
 
@@ -1825,6 +1839,29 @@
         }
     }
 
+    /**
+     * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+     * schedule {@link InputMethodManager#restartInput(View)}.
+     */
+    void scheduleRestartInputForSetText() {
+        mHasPendingRestartInputForSetText = true;
+    }
+
+    /**
+     * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+     * actually call {@link InputMethodManager#restartInput(View)} if it's scheduled.  Does nothing
+     * otherwise.
+     */
+    void maybeFireScheduledRestartInputForSetText() {
+        if (mHasPendingRestartInputForSetText) {
+            final InputMethodManager imm = getInputMethodManager();
+            if (imm != null) {
+                imm.restartInput(mTextView);
+            }
+            mHasPendingRestartInputForSetText = false;
+        }
+    }
+
     static final int EXTRACT_NOTHING = -2;
     static final int EXTRACT_UNKNOWN = -1;
 
@@ -1958,7 +1995,8 @@
     }
 
     private void sendUpdateSelection() {
-        if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
+        if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0
+                && !mHasPendingRestartInputForSetText) {
             final InputMethodManager imm = getInputMethodManager();
             if (null != imm) {
                 final int selectionStart = mTextView.getSelectionStart();
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 105c714..b5a5848 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -706,8 +706,7 @@
                 if (getChildCount() == 0) {
                     return false;
                 }
-                if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowRight.isFinished()
-                        || !mEdgeGlowLeft.isFinished())) {
+                if (!mScroller.isFinished()) {
                     final ViewParent parent = getParent();
                     if (parent != null) {
                         parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 65f3da7..2dd7f02 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -763,8 +763,7 @@
                 if (getChildCount() == 0) {
                     return false;
                 }
-                if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowTop.isFinished()
-                        || !mEdgeGlowBottom.isFinished())) {
+                if (!mScroller.isFinished()) {
                     final ViewParent parent = getParent();
                     if (parent != null) {
                         parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 07a9a5fa..1a37b59 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6285,11 +6285,10 @@
                 || needEditableForNotification) {
             createEditorIfNeeded();
             mEditor.forgetUndoRedo();
+            mEditor.scheduleRestartInputForSetText();
             Editable t = mEditableFactory.newEditable(text);
             text = t;
             setFilters(t, mFilters);
-            InputMethodManager imm = getInputMethodManager();
-            if (imm != null) imm.restartInput(this);
         } else if (precomputed != null) {
             if (mTextDir == null) {
                 mTextDir = getTextDirectionHeuristic();
@@ -6408,8 +6407,12 @@
             notifyListeningManagersAfterTextChanged();
         }
 
-        // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
-        if (mEditor != null) mEditor.prepareCursorControllers();
+        if (mEditor != null) {
+            // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
+            mEditor.prepareCursorControllers();
+
+            mEditor.maybeFireScheduledRestartInputForSetText();
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/graphics/OWNERS b/core/java/com/android/internal/graphics/OWNERS
new file mode 100644
index 0000000..5851cbb
--- /dev/null
+++ b/core/java/com/android/internal/graphics/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/OWNERS
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 070e6ab..11df5a8 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -43,7 +43,6 @@
     void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
     void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
     void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
-    void notifyUserAction(in IVoidResultCallback resultCallback);
-    void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible,
-            in IVoidResultCallback resultCallback);
+    void notifyUserActionAsync();
+    void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 2fc8d7a..ed1fe1a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -359,26 +359,23 @@
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction(IVoidResultCallback)}
+     * Calls {@link IInputMethodPrivilegedOperations#notifyUserActionAsync()}
      */
     @AnyThread
-    public void notifyUserAction() {
+    public void notifyUserActionAsync() {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
-            final Completable.Void value = Completable.createVoid();
-            ops.notifyUserAction(ResultCallbacks.of(value));
-            Completable.getResult(value);
+            ops.notifyUserActionAsync();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean,
-     * IVoidResultCallback)}.
+     * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibilityAsync(IBinder, boolean)}.
      *
      * @param showOrHideInputToken placeholder token that maps to window requesting
      *        {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
@@ -387,15 +384,13 @@
      * @param setVisible {@code true} to set IME visible, else hidden.
      */
     @AnyThread
-    public void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible) {
+    public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
         }
         try {
-            final Completable.Void value = Completable.createVoid();
-            ops.applyImeVisibility(showOrHideInputToken, setVisible, ResultCallbacks.of(value));
-            Completable.getResult(value);
+            ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 04528e9..c32951a 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,8 @@
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
 
+#include <mutex>
+
 #include "signal.h"
 
 #include "android-base/logging.h"
@@ -26,6 +28,7 @@
 #include "utils/misc.h"
 #include "utils/Trace.h"
 
+#include "android_content_res_ApkAssets.h"
 #include "android_util_AssetManager_private.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
@@ -72,6 +75,19 @@
   FORMAT_DIRECTORY = 3,
 };
 
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr) {
+    return *reinterpret_cast<Guarded<std::unique_ptr<const ApkAssets>>*>(ptr);
+}
+
+static jlong CreateGuardedApkAssets(std::unique_ptr<const ApkAssets> assets) {
+    auto guarded_assets = new Guarded<std::unique_ptr<const ApkAssets>>(std::move(assets));
+    return reinterpret_cast<jlong>(guarded_assets);
+}
+
+static void DeleteGuardedApkAssets(Guarded<std::unique_ptr<const ApkAssets>>& apk_assets) {
+    delete &apk_assets;
+}
+
 class LoaderAssetsProvider : public AssetsProvider {
  public:
   static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
@@ -227,7 +243,7 @@
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -279,7 +295,7 @@
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -347,12 +363,12 @@
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
   auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
-  return reinterpret_cast<jlong>(apk_assets.release());
+  return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
 // STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
@@ -383,54 +399,60 @@
 }
 
 static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  destroy_info << "{ptr=" << apk_assets;
-  if (apk_assets != nullptr) {
-    destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
-                 << ", idmap=" << apk_assets->GetLoadedIdmap()
-                 << ", {arsc=" << apk_assets->GetLoadedArsc();
-    if (auto arsc = apk_assets->GetLoadedArsc()) {
-      destroy_info << ", strings=" << arsc->GetStringPool()
-                   << ", packages=" << &arsc->GetPackages()
-                   << " [";
-      for (auto& package : arsc->GetPackages()) {
-        destroy_info << "{unique_ptr=" << &package
-                     << ", package=" << package.get() << "},";
-      }
-      destroy_info << "]";
+    {
+        auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+        auto apk_assets = scoped_apk_assets->get();
+        destroy_info << "{ptr=" << apk_assets;
+        if (apk_assets != nullptr) {
+            destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
+                         << ", idmap=" << apk_assets->GetLoadedIdmap()
+                         << ", {arsc=" << apk_assets->GetLoadedArsc();
+            if (auto arsc = apk_assets->GetLoadedArsc()) {
+                destroy_info << ", strings=" << arsc->GetStringPool()
+                             << ", packages=" << &arsc->GetPackages() << " [";
+                for (auto& package : arsc->GetPackages()) {
+                    destroy_info << "{unique_ptr=" << &package << ", package=" << package.get()
+                                 << "},";
+                }
+                destroy_info << "]";
+            }
+            destroy_info << "}";
+        }
+        destroy_info << "}";
     }
-    destroy_info << "}";
-  }
-  destroy_info << "}";
 
-  delete apk_assets;
+    DeleteGuardedApkAssets(ApkAssetsFromLong(ptr));
 
-  // Deleting the apk assets did not lead to a crash.
-  destroy_info.str("");
-  destroy_info.clear();
+    // Deleting the apk assets did not lead to a crash.
+    destroy_info.str("");
+    destroy_info.clear();
 }
 
 static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
-  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  if (auto path = apk_assets->GetPath()) {
-    return env->NewStringUTF(path->data());
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    if (auto path = apk_assets->GetPath()) {
+        return env->NewStringUTF(path->data());
   }
   return nullptr;
 }
 
 static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
-  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  return env->NewStringUTF(apk_assets->GetDebugName().c_str());
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    return env->NewStringUTF(apk_assets->GetDebugName().c_str());
 }
 
 static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
 }
 
 static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
-  return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
+    return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
 }
 
 static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@@ -439,7 +461,8 @@
     return 0;
   }
 
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+  auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+  auto apk_assets = scoped_apk_assets->get();
   std::unique_ptr<Asset> asset = apk_assets->GetAssetsProvider()->Open(
       path_utf8.c_str(),Asset::AccessMode::ACCESS_RANDOM);
   if (asset == nullptr) {
@@ -467,12 +490,13 @@
 
 static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
                                          jstring overlayable_name) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
 
-  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
-  if (packages.empty()) {
-    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
-    return 0;
+    const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+    if (packages.empty()) {
+        jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+        return 0;
   }
 
   // TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
@@ -502,13 +526,14 @@
 }
 
 static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
-  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+    auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+    auto apk_assets = scoped_apk_assets->get();
 
-  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
-  if (packages.empty()) {
-    // Must throw to prevent bypass by returning false
-    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
-    return 0;
+    const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+    if (packages.empty()) {
+        // Must throw to prevent bypass by returning false
+        jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+        return 0;
   }
 
   const auto& overlayable_infos = packages[0]->GetOverlayableMap();
diff --git a/core/jni/android_content_res_ApkAssets.h b/core/jni/android_content_res_ApkAssets.h
new file mode 100644
index 0000000..7e525dc
--- /dev/null
+++ b/core/jni/android_content_res_ApkAssets.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CONTENT_RES_APKASSETS_H
+#define ANDROID_CONTENT_RES_APKASSETS_H
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/MutexGuard.h"
+
+#include "jni.h"
+
+namespace android {
+
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr);
+
+} // namespace android
+
+#endif // ANDROID_CONTENT_RES_APKASSETS_H
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2c69a0..24f9abf 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -42,6 +42,7 @@
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/ResourceUtils.h"
 
+#include "android_content_res_ApkAssets.h"
 #include "android_util_AssetManager_private.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
@@ -50,9 +51,9 @@
 #include "nativehelper/ScopedStringChars.h"
 #include "nativehelper/ScopedUtfChars.h"
 #include "utils/Log.h"
-#include "utils/misc.h"
 #include "utils/String8.h"
 #include "utils/Trace.h"
+#include "utils/misc.h"
 
 extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
 extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
@@ -307,7 +308,9 @@
     if (env->ExceptionCheck()) {
       return;
     }
-    apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+
+    auto scoped_assets = ScopedLock(ApkAssetsFromLong(apk_assets_native_ptr));
+    apk_assets.push_back(scoped_assets->get());
   }
 
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ae6dcfd..1bba12f 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -430,6 +430,7 @@
         optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto one_handed_mode_activated = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional OneHanded onehanded = 80;
 
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 9e1692f..e7174cc 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -18,7 +18,7 @@
     android:id="@+id/work_widget_mask_frame"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="#F3374248"
+    android:background="?android:attr/colorSurfaceVariant"
     android:importantForAccessibility="noHideDescendants"
     android:clickable="true">
 
@@ -33,8 +33,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom|right"
-        android:layout_marginBottom="4dp"
-        android:layout_marginRight="4dp"
+        android:layout_marginBottom="12dp"
+        android:layout_marginRight="12dp"
         android:src="@drawable/ic_corp_badge_off"
         android:clickable="false" />
 </FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f097009..d4fa285 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9576,8 +9576,9 @@
     <attr name="lStar" format="float"/>
 
     <declare-styleable name="DisplayHashingService">
-        <!-- The throttle duration for display hash requests
+        <!-- The interval required between display hash requests. Requests made faster than this
+             delay will be throttled."
              @hide @SystemApi -->
-        <attr name="throttleDurationMillis" format="integer" />
+        <attr name="durationBetweenRequestsMillis" format="integer" />
     </declare-styleable>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fbc9678..d3ea52e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4892,4 +4892,7 @@
     <bool name="config_supportsMicToggle">false</bool>
     <!-- Whether this device is supporting the camera toggle -->
     <bool name="config_supportsCamToggle">false</bool>
+
+    <!-- List containing the allowed install sources for accessibility service. -->
+    <string-array name="config_accessibility_allowed_install_source" translatable="false"/>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c716000..0e9526a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3097,7 +3097,7 @@
     <public name="playHomeTransitionSound" />
     <public name="lStar" />
     <!-- @hide @SystemApi -->
-    <public name="throttleDurationMillis" />
+    <public name="durationBetweenRequestsMillis" />
     <public name="showInInputMethodPicker" />
     <public name="effectColor" />
   </staging-public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8d29d4..b9f1e20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4357,6 +4357,8 @@
   <java-symbol type="drawable" name="ic_accessibility_24dp" />
   <java-symbol type="string" name="view_and_control_notification_title" />
   <java-symbol type="string" name="view_and_control_notification_content" />
+  <java-symbol type="array" name="config_accessibility_allowed_install_source" />
+  
   <!-- Translation -->
   <java-symbol type="string" name="ui_translation_accessibility_translated_text" />
   <java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index b6d408a..eb82c6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,7 +23,6 @@
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 import android.window.WindowContainerToken;
@@ -89,11 +88,11 @@
         ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
                 task1.taskId, task2.taskId, this);
 
-        if ((!task1.isResizeable || !task2.isResizeable)
-                && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
+        if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) {
             ProtoLog.e(WM_SHELL_TASK_ORG,
-                    "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
-                    task1.isResizeable, task2.isResizeable);
+                    "Can't pair tasks that doesn't support multi window, "
+                            + "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b",
+                    task1.supportsMultiWindow, task2.supportsMultiWindow);
             return false;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 6719d74..0f5d0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1604,6 +1604,7 @@
             // can start fresh.
             cancelAllExpandCollapseSwitchAnimations();
         }
+        showManageMenu(false /* show */);
 
         // If we're expanded, screenshot the currently expanded bubble (before expanding the newly
         // selected bubble) so we can animate it out.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index b9fdaa1..442e7a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -110,6 +110,10 @@
             return false;
         }
 
+        if (mDoubleTapDetector.onTouchEvent(event)) {
+            return true;
+        }
+
         final int action = event.getAction() & MotionEvent.ACTION_MASK;
         final boolean isLandscape = isLandscape();
         // Using raw xy to prevent lost track of motion events while moving divider bar.
@@ -136,21 +140,22 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mVelocityTracker.addMovement(event);
+                releaseTouching();
+
+                if (!mMoving) break;
+
                 mVelocityTracker.computeCurrentVelocity(1000 /* units */);
                 final float velocity = isLandscape
                         ? mVelocityTracker.getXVelocity()
                         : mVelocityTracker.getYVelocity();
-                releaseTouching();
-                mMoving = false;
-
                 final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
                 final DividerSnapAlgorithm.SnapTarget snapTarget =
                         mSplitLayout.findSnapTarget(position, velocity, false /* hardDismiss */);
                 mSplitLayout.snapToTarget(position, snapTarget);
+                mMoving = false;
                 break;
         }
 
-        mDoubleTapDetector.onTouchEvent(event);
         return true;
     }
 
@@ -229,7 +234,7 @@
             if (mSplitLayout != null) {
                 mSplitLayout.onDoubleTappedDivider();
             }
-            return false;
+            return true;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b64c796..d318a5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -231,6 +231,7 @@
     }
 
     private void flingDividePosition(int from, int to) {
+        if (from == to) return;
         ValueAnimator animator = ValueAnimator
                 .ofInt(from, to)
                 .setDuration(250);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index a18d106..60f7ee2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -49,7 +49,6 @@
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowManager;
@@ -524,9 +523,10 @@
             case MotionEvent.ACTION_CANCEL:
                 mVelocityTracker.addMovement(event);
 
+                if (!mMoving) break;
+
                 x = (int) event.getRawX();
                 y = (int) event.getRawY();
-
                 mVelocityTracker.computeCurrentVelocity(1000);
                 int position = calculatePosition(x, y);
                 stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index 27c56fd..d9409ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -30,7 +30,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -92,11 +91,10 @@
                         // is nothing behind it.
                         ((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
                                 && triggerTask.parentTaskId == mListener.mPrimary.taskId)
-                        // if a non-resizable is launched when it is not supported in multi window,
+                        // if an activity that is not supported in multi window mode is launched,
                         // we also need to leave split-screen.
                         || ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
-                                && !triggerTask.isResizeable
-                                && !ActivityTaskManager.supportsNonResizableMultiWindow());
+                                && !triggerTask.supportsMultiWindow);
                 // In both cases, dismiss the primary
                 if (shouldDismiss) {
                     WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 5a2ef56..1072845 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,7 +46,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.BooleanSupplier;
 
 /**
  * Proxy to simplify calls into window manager/activity manager
@@ -209,17 +208,10 @@
             return false;
         }
         ActivityManager.RunningTaskInfo topHomeTask = null;
-        // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
-        // because the value can be changed at runtime.
-        final BooleanSupplier supportsNonResizableMultiWindow =
-                createSupportsNonResizableMultiWindowSupplier();
         for (int i = rootTasks.size() - 1; i >= 0; --i) {
             final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
-            // Check whether to move resizeable task to split secondary.
-            // Also, we have an exception for non-resizable home because we will minimize to show
-            // it.
-            if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
-                    && !supportsNonResizableMultiWindow.getAsBoolean()) {
+            // Check whether the task can be moved to split secondary.
+            if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
                 continue;
             }
             // Only move fullscreen tasks to split secondary.
@@ -364,21 +356,6 @@
         outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
     }
 
-    /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
-    private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
-        return new BooleanSupplier() {
-            private Boolean mSupportsNonResizableMultiWindow;
-            @Override
-            public boolean getAsBoolean() {
-                if (mSupportsNonResizableMultiWindow == null) {
-                    mSupportsNonResizableMultiWindow =
-                            ActivityTaskManager.supportsNonResizableMultiWindow();
-                }
-                return mSupportsNonResizableMultiWindow;
-            }
-        };
-    }
-
     /**
      * Utility to apply a sync transaction serially with other sync transactions.
      *
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 134d00b..741773e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -22,6 +22,7 @@
 import android.system.helpers.ActivityHelper
 import android.util.Log
 import android.view.Surface
+import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -138,7 +139,7 @@
         append("$primaryApp $secondaryApp")
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 186510496)
     @Test
     open fun navBarLayerIsAlwaysVisible() {
         testSpec.navBarLayerIsAlwaysVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index a7e1d0f..95672f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -65,7 +65,7 @@
     @Test
     fun pipLayerBecomesVisible() {
         testSpec.assertLayers {
-            this.isVisible(pipApp.launcherName)
+            this.isVisible(pipApp.windowName)
         }
     }
 
@@ -73,9 +73,11 @@
     @Test
     fun pipWindowBecomesVisible() {
         testSpec.assertWm {
-            invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
-                .then()
-                .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+            invoke("pipWindowIsNotVisible") {
+                verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue()
+            }.then().invoke("pipWindowIsVisible") {
+                verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue()
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index b0de029..2b5cd60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -69,6 +69,7 @@
         info.configuration.windowConfiguration.setActivityType(mActivityType);
         info.token = mToken;
         info.isResizeable = true;
+        info.supportsMultiWindow = true;
         return info;
     }
 }
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 2a70f0d..cb620cc 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -70,9 +70,6 @@
 
 }  // namespace
 
-LoadedPackage::LoadedPackage() = default;
-LoadedPackage::~LoadedPackage() = default;
-
 // Precondition: The header passed in has already been verified, so reading any fields and trusting
 // the ResChunk_header is safe.
 static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 119f531..10666ad 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -94,6 +94,7 @@
   };
 
   AssetManager2();
+  explicit AssetManager2(AssetManager2&& other) = default;
 
   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
   // are not owned by the AssetManager, and must have a longer lifetime.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index d9225cd..3b222c5 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -153,8 +153,6 @@
   static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
                                                    package_property_t property_flags);
 
-  ~LoadedPackage();
-
   // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
   // the underlying ResStringPool API expects this. For now this is acceptable, but since
   // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -275,7 +273,7 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
 
-  LoadedPackage();
+  LoadedPackage() = default;
 
   ResStringPool type_string_pool_;
   ResStringPool key_string_pool_;
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f4..6fc6d64 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -18,6 +18,7 @@
 #define ANDROIDFW_MUTEXGUARD_H
 
 #include <mutex>
+#include <optional>
 #include <type_traits>
 
 #include "android-base/macros.h"
@@ -47,34 +48,32 @@
   static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
 
  public:
-  explicit Guarded() : guarded_() {
+  Guarded() : guarded_(std::in_place, T()) {
   }
 
-  template <typename U = T>
-  explicit Guarded(const T& guarded,
-                   typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
-      : guarded_(guarded) {
+  explicit Guarded(const T& guarded) : guarded_(std::in_place, guarded) {
   }
 
-  template <typename U = T>
-  explicit Guarded(T&& guarded,
-                   typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
-      : guarded_(std::move(guarded)) {
+  explicit Guarded(T&& guarded) : guarded_(std::in_place, std::forward<T>(guarded)) {
+  }
+
+  ~Guarded() {
+    std::lock_guard<std::mutex> scoped_lock(lock_);
+    guarded_.reset();
   }
 
  private:
   friend class ScopedLock<T>;
-
   DISALLOW_COPY_AND_ASSIGN(Guarded);
 
   std::mutex lock_;
-  T guarded_;
+  std::optional<T> guarded_;
 };
 
 template <typename T>
 class ScopedLock {
  public:
-  explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+  explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(*guarded.guarded_) {
   }
 
   T& operator*() {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index f5641bd..83a838f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -26,6 +26,7 @@
 import androidx.annotation.Nullable;
 
 import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.google.android.material.resources.TextAppearanceConfig;
 
 /**
  * A base Activity that has a collapsing toolbar layout is used for the activities intending to
@@ -39,7 +40,8 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
+        // Force loading font synchronously for collapsing toolbar layout
+        TextAppearanceConfig.setShouldLoadFontSynchronously(true);
         super.setContentView(R.layout.collapsing_toolbar_base_layout);
         mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
 
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 0287b1f..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -19,9 +19,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingStart="20dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingBottom="16dp"
+    android:paddingTop="8dp"
     android:background="?android:attr/selectableItemBackground"
     android:clipToPadding="false">
 
@@ -29,8 +30,6 @@
         android:id="@android:id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingBottom="16dp"
-        android:paddingTop="16dp"
         android:clickable="false"
         android:longClickable="false"
         android:maxLines="10"
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index fbb84fd..ccbcb89 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -256,4 +256,7 @@
     <!-- Default for Settings.Secure.ACCESSIBILITY_BUTTON_MODE -->
     <integer name="def_accessibility_button_mode">1</integer>
 
+    <!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
+    <bool name="def_one_handed_mode_activated">false</bool>
+
 </resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 72fb237..1cfdff8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+        Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
         Settings.Secure.ONE_HANDED_MODE_ENABLED,
         Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
         Settings.Secure.TAPS_APP_TO_EXIT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4872aa4..36f5dba 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -262,6 +262,7 @@
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+        VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2e90d36..941f47f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3401,7 +3401,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 201;
+            private static final int SETTINGS_VERSION = 202;
 
             private final int mUserId;
 
@@ -4959,6 +4959,22 @@
                     currentVersion = 201;
                 }
 
+                if (currentVersion == 201) {
+                    // Version 201: Set the default value for Secure Settings:
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting oneHandedModeActivated = secureSettings.getSettingLocked(
+                            Secure.ONE_HANDED_MODE_ACTIVATED);
+                    if (oneHandedModeActivated.isNull()) {
+                        final boolean defOneHandedModeActivated = getContext().getResources()
+                                .getBoolean(R.bool.def_one_handed_mode_activated);
+                        secureSettings.insertSettingLocked(
+                                Secure.ONE_HANDED_MODE_ACTIVATED,
+                                defOneHandedModeActivated ? "1" : "0", null, true,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 202;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index c3f1113..666ec27 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -21,5 +21,6 @@
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
-    android:clipChildren="true"
+    android:clipChildren="false"
+    android:clipToPadding="false"
     android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 30e52e9..2cafd1b 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -36,6 +36,8 @@
         android:elevation="4dp"
         android:importantForAccessibility="no"
         android:scrollbars="none"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:layout_weight="1">
         <com.android.systemui.qs.QSPanel
             android:id="@+id/quick_settings_panel"
@@ -43,7 +45,9 @@
             android:layout_height="wrap_content"
             android:background="@android:color/transparent"
             android:focusable="true"
-            android:accessibilityTraversalBefore="@android:id/edit">
+            android:accessibilityTraversalBefore="@android:id/edit"
+            android:clipToPadding="false"
+            android:clipChildren="false">
             <include layout="@layout/qs_footer_impl" />
             <include layout="@layout/qs_media_divider"
                 android:id="@+id/divider"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8601bdc..bbb5519 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2727,7 +2727,7 @@
     <string name="accessibility_floating_button_action_move_bottom_right">Move bottom right</string>
     <!-- Action in accessibility menu to move the accessibility floating button to the edge and hide it to half. [CHAR LIMIT=30]-->
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half">Move to edge and hide</string>
-    <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=30]-->
+    <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=36]-->
     <string name="accessibility_floating_button_action_move_out_edge_and_show">Move out edge and show</string>
 
     <!-- Device Controls strings -->
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 60b677a..825e008 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -21,12 +21,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
 import android.icu.text.NumberFormat;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.ViewController;
 
@@ -35,13 +37,15 @@
 import java.util.TimeZone;
 
 /**
- * Controller for an AnimatableClockView.
+ * Controller for an AnimatableClockView. Instantiated by {@link KeyguardClockSwitchController}.
  */
 public class AnimatableClockController extends ViewController<AnimatableClockView> {
     private static final int FORMAT_NUMBER = 1234567890;
 
     private final StatusBarStateController mStatusBarStateController;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardBypassController mBypassController;
     private final int mDozingColor = Color.WHITE;
     private int mLockScreenColor;
 
@@ -59,12 +63,16 @@
             AnimatableClockView view,
             StatusBarStateController statusBarStateController,
             BroadcastDispatcher broadcastDispatcher,
-            BatteryController batteryController) {
+            BatteryController batteryController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController bypassController) {
         super(view);
         mStatusBarStateController = statusBarStateController;
         mIsDozing = mStatusBarStateController.isDozing();
         mDozeAmount = mStatusBarStateController.getDozeAmount();
         mBroadcastDispatcher = broadcastDispatcher;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mBypassController = bypassController;
 
         mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
         mBurmeseLineSpacing = getContext().getResources().getFloat(
@@ -98,14 +106,29 @@
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mIsDozing = mStatusBarStateController.isDozing();
         mDozeAmount = mStatusBarStateController.getDozeAmount();
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+
         refreshTime();
         initColors();
     }
 
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+                boolean isStrongBiometric) {
+            if (biometricSourceType == BiometricSourceType.FACE
+                    && mBypassController.canBypass()) {
+                mView.animateDisappear();
+            }
+        }
+    };
+
     @Override
     protected void onViewDetached() {
         mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 0d6f64f..dc14f82 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,6 +27,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
 import java.util.Calendar;
 import java.util.TimeZone;
@@ -155,6 +156,21 @@
         mLockScreenColor = lockScreenColor;
     }
 
+    void animateDisappear() {
+        if (mTextAnimator == null) {
+            return;
+        }
+
+        setTextStyle(
+                0 /* weight */,
+                -1 /* text size, no update */,
+                null /* color, no update */,
+                true /* animate */,
+                KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
+                0 /* delay */,
+                null /* onAnimationEnd */);
+    }
+
     void animateCharge(boolean isDozing) {
         if (mTextAnimator == null || mTextAnimator.isRunning()) {
             // Skip charge animation if dozing animation is already playing.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5559a18..5b83551 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -90,10 +91,11 @@
 
     private SmartspaceSession mSmartspaceSession;
     private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
-    private int mWallpaperTextColor;
     private ConfigurationController mConfigurationController;
     private ActivityStarter mActivityStarter;
     private FalsingManager mFalsingManager;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardBypassController mBypassController;
 
     /**
      * Listener for changes to the color palette.
@@ -147,7 +149,9 @@
             ConfigurationController configurationController,
             SystemUIFactory systemUIFactory,
             ActivityStarter activityStarter,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController bypassController) {
         super(keyguardClockSwitch);
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
@@ -162,6 +166,8 @@
         mSystemUIFactory = systemUIFactory;
         mActivityStarter = activityStarter;
         mFalsingManager = falsingManager;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mBypassController = bypassController;
     }
 
     /**
@@ -185,19 +191,23 @@
         mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
 
         mClockViewController =
-            new AnimatableClockController(
-                mView.findViewById(R.id.animatable_clock_view),
-                mStatusBarStateController,
-                mBroadcastDispatcher,
-                mBatteryController);
+                new AnimatableClockController(
+                        mView.findViewById(R.id.animatable_clock_view),
+                        mStatusBarStateController,
+                        mBroadcastDispatcher,
+                        mBatteryController,
+                        mKeyguardUpdateMonitor,
+                        mBypassController);
         mClockViewController.init();
 
         mLargeClockViewController =
-            new AnimatableClockController(
-                mView.findViewById(R.id.animatable_clock_view_large),
-                mStatusBarStateController,
-                mBroadcastDispatcher,
-                mBatteryController);
+                new AnimatableClockController(
+                        mView.findViewById(R.id.animatable_clock_view_large),
+                        mStatusBarStateController,
+                        mBroadcastDispatcher,
+                        mBatteryController,
+                        mKeyguardUpdateMonitor,
+                        mBypassController);
         mLargeClockViewController.init();
 
         mStatusBarStateController.addCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 2bd7729..9163044 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceTarget
 import android.os.SystemProperties
 import android.util.Log
@@ -121,6 +122,12 @@
         }
 
         // If no recent media, continue with smartspace update
+        if (isMediaRecommendationEmpty(data)) {
+            Log.d(TAG, "Empty media recommendations. Skip showing the card")
+            return
+        }
+
+        // Proceed only if the Smartspace recommendation is not empty.
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
     }
 
@@ -214,4 +221,10 @@
      * Remove a listener that was registered with addListener
      */
     fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener)
+
+    /** Check if the Smartspace sends an empty update. */
+    private fun isMediaRecommendationEmpty(data: SmartspaceTarget): Boolean {
+        val mediaRecommendationList: List<SmartspaceAction> = data.getIconGrid()
+        return mediaRecommendationList == null || mediaRecommendationList.isEmpty()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index f7fa5bf..c552e89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -8,7 +8,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -63,7 +62,6 @@
     private int mPageToRestore = -1;
     private int mLayoutOrientation;
     private int mLayoutDirection;
-    private final Rect mClippingRect;
     private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
     private int mExcessHeight;
     private int mLastExcessHeight;
@@ -78,14 +76,16 @@
         setCurrentItem(0, false);
         mLayoutOrientation = getResources().getConfiguration().orientation;
         mLayoutDirection = getLayoutDirection();
-        mClippingRect = new Rect();
-
-        // Make sure there's a space between pages when scroling
-        setPageMargin(context.getResources().getDimensionPixelOffset(
-                    R.dimen.qs_tile_margin_horizontal));
     }
     private int mLastMaxHeight = -1;
 
+    @Override
+    public void setPageMargin(int marginPixels) {
+        if (marginPixels != getPageMargin()) {
+            super.setPageMargin(marginPixels);
+        }
+    }
+
     public void saveInstanceState(Bundle outState) {
         outState.putInt(CURRENT_PAGE, getCurrentItem());
     }
@@ -353,14 +353,6 @@
     }
 
     @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        // Clip to margins
-        mClippingRect.set(0, 0, (r - l), b - t);
-        setClipBounds(mClippingRect);
-    }
-
-    @Override
     public boolean setMinRows(int minRows) {
         mMinRows = minRows;
         boolean changed = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 661dbf6..9abb430 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -314,6 +314,9 @@
             if (view == mQSPanelContainer) {
                 // QS panel lays out some of its content full width
                 qsPanelController.setContentMargins(mContentPadding, mContentPadding);
+                // Set it as double the side margin (to simulate end margin of current page +
+                // start margin of next page).
+                qsPanelController.setPageMargin(2 * mSideMargins);
             } else if (view == mHeader) {
                 // The header contains the QQS panel which needs to have special padding, to
                 // visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d5cb777..814846c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -480,8 +480,12 @@
     private void updateQsBounds() {
         if (mLastQSExpansion == 1.0f) {
             // Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
-            // it's a scrollview and otherwise wouldn't be clipped.
-            mQsBounds.set(0, 0, mQSPanelScrollView.getWidth(), mQSPanelScrollView.getHeight());
+            // it's a scrollview and otherwise wouldn't be clipped. However, we set the horizontal
+            // bounds so the pages go to the ends of QSContainerImpl
+            ViewGroup.MarginLayoutParams lp =
+                    (ViewGroup.MarginLayoutParams) mQSPanelScrollView.getLayoutParams();
+            mQsBounds.set(-lp.leftMargin, 0, mQSPanelScrollView.getWidth() + lp.rightMargin,
+                    mQSPanelScrollView.getHeight());
         }
         mQSPanelScrollView.setClipBounds(mQsBounds);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f7b6686..4e16b74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -672,6 +672,16 @@
         switchSecurityFooter();
     }
 
+    protected void setPageMargin(int pageMargin) {
+        if (mRegularTileLayout instanceof PagedTileLayout) {
+            ((PagedTileLayout) mRegularTileLayout).setPageMargin(pageMargin);
+        }
+        if (mHorizontalTileLayout != mRegularTileLayout
+                && mHorizontalTileLayout instanceof PagedTileLayout) {
+            ((PagedTileLayout) mHorizontalTileLayout).setPageMargin(pageMargin);
+        }
+    }
+
     void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force,
             UiEventLogger uiEventLogger) {
         if (horizontal != mUsingHorizontalLayout || force) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 93ccfc72..fff3d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -321,5 +321,9 @@
     public boolean isExpanded() {
         return mView.isExpanded();
     }
+
+    void setPageMargin(int pageMargin) {
+        mView.setPageMargin(pageMargin);
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index a4bb095..7c0496b 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.sensorprivacy
 
-import android.app.KeyguardManager
-import android.app.KeyguardManager.KeyguardDismissCallback
 import android.content.DialogInterface
 import android.content.Intent.EXTRA_PACKAGE_NAME
 import android.content.pm.PackageManager
@@ -28,16 +26,15 @@
 import android.os.Bundle
 import android.os.Handler
 import android.text.Html
-import android.util.Log
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import android.widget.ImageView
 import com.android.internal.app.AlertActivity
 import com.android.internal.widget.DialogTitle
-import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
-
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
 
 /**
@@ -48,8 +45,8 @@
  */
 class SensorUseStartedActivity @Inject constructor(
     private val sensorPrivacyController: IndividualSensorPrivacyController,
-    private val keyguardManager: KeyguardManager,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+    private val keyguardStateController: KeyguardStateController,
+    private val keyguardDismissUtil: KeyguardDismissUtil
 ) : AlertActivity(), DialogInterface.OnClickListener {
 
     companion object {
@@ -180,17 +177,11 @@
     override fun onClick(dialog: DialogInterface?, which: Int) {
         when (which) {
             BUTTON_POSITIVE -> {
-                if (keyguardUpdateMonitor.getUserHasTrust(userId)) {
-                    keyguardManager
-                            .requestDismissKeyguard(this, object : KeyguardDismissCallback() {
-                        override fun onDismissError() {
-                            Log.e(LOG_TAG, "Cannot dismiss keyguard")
-                        }
-
-                        override fun onDismissSucceeded() {
-                            disableSensorPrivacy()
-                        }
-                    })
+                if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
+                    keyguardDismissUtil.executeWhenUnlocked({
+                        disableSensorPrivacy()
+                        false
+                    }, false)
                 } else {
                     disableSensorPrivacy()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 761a1d6..91415f27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -57,7 +57,7 @@
     private val windowManager: WindowManager,
     private val systemClock: SystemClock
 ) {
-    private var charging: Boolean? = null
+    private var pluggedIn: Boolean? = null
     private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled &&
             !SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false)
     private var normalizedPortPosX: Float = context.resources.getFloat(
@@ -86,18 +86,17 @@
         val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
             override fun onBatteryLevelChanged(
                 level: Int,
-                pluggedIn: Boolean,
-                nowCharging: Boolean
+                nowPluggedIn: Boolean,
+                charging: Boolean
             ) {
                 // Suppresses the ripple when it's disabled, or when the state change comes
                 // from wireless charging.
-                if (!rippleEnabled || batteryController.isWirelessCharging) {
+                if (!rippleEnabled || batteryController.isPluggedInWireless) {
                     return
                 }
-                val wasCharging = charging
-                charging = nowCharging
-                // Only triggers when the keyguard is active and the device is just plugged in.
-                if ((wasCharging == null || !wasCharging) && nowCharging) {
+                val wasPluggedIn = pluggedIn
+                pluggedIn = nowPluggedIn
+                if ((wasPluggedIn == null || !wasPluggedIn) && nowPluggedIn) {
                     startRippleWithDebounce()
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 30d9841..26c6fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -217,7 +217,7 @@
     }
 
     companion object {
-        const val BYPASS_PANEL_FADE_DURATION = 67
+        const val BYPASS_FADE_DURATION = 67
 
         private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
         private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f319022..83a64f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2060,7 +2060,8 @@
         int bottom = 0;
         int left = 0;
         int right = 0;
-        boolean visible = qsFraction > 0 || qsPanelBottomY > 0;
+        boolean visible = (qsFraction > 0 || qsPanelBottomY > 0)
+                && !mShouldUseSplitNotificationShade;
         int radius = mScrimCornerRadius;
         if (visible || !mShouldUseSplitNotificationShade) {
             if (!mShouldUseSplitNotificationShade) {
@@ -2079,11 +2080,11 @@
             }
         }
 
+        // Fancy clipping for quick settings
+        if (mQs != null) {
+            mQs.setFancyClipping(top, bottom, radius, visible);
+        }
         if (!mShouldUseSplitNotificationShade) {
-            // Fancy clipping for quick settings
-            if (mQs != null) {
-                mQs.setFancyClipping(top, bottom, radius, visible);
-            }
             // The padding on this area is large enough that we can use a cheaper clipping strategy
             mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
             mKeyguardStatusViewController.setClipBounds(visible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 14aeca1..f403cc94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -615,7 +615,7 @@
             boolean needsFading = needsBypassFading();
             if (needsFading) {
                 delay = 0;
-                fadeoutDuration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION;
+                fadeoutDuration = KeyguardBypassController.BYPASS_FADE_DURATION;
             } else if (wakeUnlockPulsing) {
                 delay = 0;
                 fadeoutDuration = 240;
@@ -979,7 +979,6 @@
             resetAlternateAuth(false);
             executeAfterKeyguardGoneAction();
         }
-
     }
 
     public void showBouncerMessage(String message, ColorStateList colorState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6ae5e90..95a7316 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -43,6 +43,13 @@
     boolean isPluggedIn();
 
     /**
+     * Returns {@code true} if the device is currently plugged in via wireless charger.
+     */
+    default boolean isPluggedInWireless() {
+        return false;
+    }
+
+    /**
      * Returns {@code true} if the device is currently in power save mode.
      */
     boolean isPowerSave();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 288eb3d..9e2c478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -72,6 +72,7 @@
 
     protected int mLevel;
     protected boolean mPluggedIn;
+    private boolean mPluggedInWireless;
     protected boolean mCharging;
     private boolean mStateUnknown = false;
     private boolean mCharged;
@@ -175,6 +176,8 @@
                     * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                     / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
             mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+            mPluggedInWireless = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
+                    == BatteryManager.BATTERY_PLUGGED_WIRELESS;
 
             final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                     BatteryManager.BATTERY_STATUS_UNKNOWN);
@@ -260,6 +263,11 @@
     }
 
     @Override
+    public boolean isPluggedInWireless() {
+        return mPluggedInWireless;
+    }
+
+    @Override
     public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
         // Need to fetch or refresh the estimate, but it may involve binder calls so offload the
         // work
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 08cdebd..d2bbcd50d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -34,6 +34,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -59,6 +60,7 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -489,12 +491,16 @@
                     ? "Dumping..."
                     : mContext.getString(R.string.heap_dump_tile_name);
             if (pmi != null) {
+                final long views = Debug.countInstancesOfClass(View.class);
+                final long enrs = Debug.countInstancesOfClass(ExpandableNotificationRow.class);
+                Log.v(TAG, String.format("updating tile state; rss=%d", pmi.currentRss));
+                Log.v(TAG, String.format("views: %d; ExpandableNotificationRows: %d", views, enrs));
                 icon.setRss(pmi.currentRss);
                 state.secondaryLabel =
                         String.format(
-                                "rss: %s / %s",
+                                "rss=%s views=%d\nenr=%d",
                                 formatBytes(pmi.currentRss * 1024),
-                                formatBytes(gm.mHeapLimit * 1024));
+                                views, enrs);
             } else {
                 icon.setRss(0);
                 state.secondaryLabel = null;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 39ebe68..5aacac2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -114,6 +115,10 @@
     ActivityStarter mActivityStarter;
     @Mock
     FalsingManager mFalsingManager;
+    @Mock
+    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    KeyguardBypassController mBypassController;
 
     private KeyguardClockSwitchController mController;
     private View mStatusArea;
@@ -152,7 +157,9 @@
                 mConfigurationController,
                 mSystemUIFactory,
                 mActivityStarter,
-                mFalsingManager
+                mFalsingManager,
+                mKeyguardUpdateMonitor,
+                mBypassController
         );
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index a9d256b..ac24cde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceTarget
 import android.graphics.Color
 import androidx.test.filters.SmallTest
@@ -72,6 +73,8 @@
     private lateinit var executor: Executor
     @Mock
     private lateinit var smartspaceData: SmartspaceTarget
+    @Mock
+    private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
 
     private lateinit var mediaDataFilter: MediaDataFilter
     private lateinit var dataMain: MediaData
@@ -97,6 +100,7 @@
             emptyList(), emptyList(), PACKAGE, null, null, device, true, null)
 
         `when`(smartspaceData.smartspaceTargetId).thenReturn(SMARTSPACE_KEY)
+        `when`(smartspaceData.iconGrid).thenReturn(listOf(smartspaceMediaRecommendationItem))
     }
 
     private fun setUser(id: Int) {
@@ -222,7 +226,7 @@
     }
 
     @Test
-    fun testOnSmartspaceMediaDataLoaded_noMedia_usesSmartspace() {
+    fun testOnSmartspaceMediaDataLoaded_noMedia_nonEmptyRecommendation_usesSmartspace() {
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
@@ -230,7 +234,18 @@
     }
 
     @Test
-    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_usesSmartspace() {
+    fun testOnSmartspaceMediaDataLoaded_noMedia_emptyRecommendation_showsNothing() {
+        `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        verify(listener, never())
+            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+        assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+    }
+
+    @Test
+    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
         val dataOld = dataMain.copy(active = false, lastActive = 0L)
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -240,6 +255,19 @@
     }
 
     @Test
+    fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
+        `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+        val dataOld = dataMain.copy(active = false, lastActive = 0L)
+        mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
+        mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+        verify(listener, never())
+            .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+        assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+    }
+
+    @Test
     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 4e404ae..03744b78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -71,8 +71,8 @@
         // Verify ripple added to window manager.
         captor.value.onBatteryLevelChanged(
                 0 /* unusedBatteryLevel */,
-                false /* plugged in */,
-                true /* charging */)
+                true /* plugged in */,
+                false /* charging */)
         val attachListenerCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
         verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index fd355d8..dc2628f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility;
 
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
@@ -24,7 +26,9 @@
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
@@ -33,11 +37,13 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
 
 import libcore.util.EmptyArray;
 
@@ -666,13 +672,66 @@
 
     /**
      * Identifies whether the accessibility service is true and designed for accessibility. An
-     * accessibility service is considered as accessibility category if
-     * {@link AccessibilityServiceInfo#isAccessibilityTool} is true.
+     * accessibility service is considered as accessibility category if meets all conditions below:
+     * <ul>
+     *     <li> {@link AccessibilityServiceInfo#isAccessibilityTool} is true</li>
+     *     <li> is installed from the trusted install source</li>
+     * </ul>
      *
      * @param serviceInfo The accessibility service's serviceInfo.
      * @return Returns true if it is a true accessibility service.
      */
     public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
-        return serviceInfo.isAccessibilityTool();
+        if (!serviceInfo.isAccessibilityTool()) {
+            return false;
+        }
+        if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
+            return hasTrustedSystemInstallSource(
+                    serviceInfo.getResolveInfo().serviceInfo.packageName);
+        }
+        return true;
+    }
+
+    /** Returns true if the {@code installedPackage} is installed from the trusted install source.
+     */
+    private boolean hasTrustedSystemInstallSource(String installedPackage) {
+        try {
+            InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo(
+                    installedPackage);
+            if (installSourceInfo == null) {
+                return false;
+            }
+            final String installSourcePackageName = installSourceInfo.getInitiatingPackageName();
+            if (installSourcePackageName == null || !mPackageManager.getPackageInfo(
+                    installSourcePackageName,
+                    0).applicationInfo.isSystemApp()) {
+                return false;
+            }
+            return isTrustedInstallSource(installSourcePackageName);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage);
+        }
+        return false;
+    }
+
+    /** Returns true if the {@code installerPackage} is a trusted install source. */
+    private boolean isTrustedInstallSource(String installerPackage) {
+        final String[] allowedInstallingSources = mContext.getResources().getStringArray(
+                com.android.internal.R.array
+                        .config_accessibility_allowed_install_source);
+
+        if (allowedInstallingSources.length == 0) {
+            //Filters unwanted default installers if no allowed install sources.
+            String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService(
+                    PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER,
+                    mCurrentUserId));
+            return !TextUtils.equals(defaultInstaller, installerPackage);
+        }
+        for (int i = 0; i < allowedInstallingSources.length; i++) {
+            if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index d7f4170..52388ff 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -33,6 +33,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.IActivityManager;
+import android.app.StatsManager;
+import android.app.StatsManager.StatsPullAtomCallback;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -46,6 +48,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -62,10 +65,12 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -177,6 +182,12 @@
                     NAMESPACE_APP_HIBERNATION,
                     ActivityThread.currentApplication().getMainExecutor(),
                     this::onDeviceConfigChanged);
+            getContext().getSystemService(StatsManager.class)
+                    .setPullAtomCallback(
+                            FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
+                            /* metadata */ null, // use default PullAtomMetadata values
+                            mBackgroundExecutor,
+                            new StatsPullAtomCallbackImpl());
         }
     }
 
@@ -272,6 +283,16 @@
             } else {
                 unhibernatePackageForUser(packageName, userId, pkgState);
             }
+            final UserLevelState stateSnapshot = new UserLevelState(pkgState);
+            final int userIdSnapshot = userId;
+            mBackgroundExecutor.execute(() -> {
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
+                        stateSnapshot.packageName,
+                        userIdSnapshot,
+                        stateSnapshot.hibernated,
+                        stateSnapshot.lastUnhibernatedMs);
+            });
             List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
             mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
         }
@@ -910,4 +931,29 @@
                     com.android.internal.R.bool.config_hibernationDeletesOatArtifactsEnabled);
         }
     }
+
+    private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+        @Override
+        public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+            if (atomTag != FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS) {
+                return StatsManager.PULL_SKIP;
+            }
+            if (isAppHibernationEnabled()) {
+                List<UserInfo> userInfos = mUserManager.getAliveUsers();
+                final int numUsers = userInfos.size();
+                for (int i = 0; i < numUsers; ++i) {
+                    final int userId = userInfos.get(i).id;
+                    if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+                        data.add(
+                                FrameworkStatsLog.buildStatsEvent(
+                                        atomTag,
+                                        getHibernatingPackagesForUser(userId).size(),
+                                        userId)
+                        );
+                    }
+                }
+            }
+            return StatsManager.PULL_SUCCESS;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
index b75b19d..68c363c 100644
--- a/services/core/java/com/android/server/apphibernation/UserLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -31,6 +31,14 @@
     @CurrentTimeMillisLong
     public long lastUnhibernatedMs;
 
+    UserLevelState() {}
+
+    UserLevelState(UserLevelState state) {
+        packageName = state.packageName;
+        hibernated = state.hibernated;
+        lastUnhibernatedMs = state.lastUnhibernatedMs;
+    }
+
     @Override
     public String toString() {
         return "UserLevelState{"
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6282aa6..418b969 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -6117,16 +6117,14 @@
 
         @BinderThread
         @Override
-        public void notifyUserAction(IVoidResultCallback resultCallback) {
-            CallbackUtils.onResult(resultCallback, () -> mImms.notifyUserAction(mToken));
+        public void notifyUserActionAsync() {
+            mImms.notifyUserAction(mToken);
         }
 
         @BinderThread
         @Override
-        public void applyImeVisibility(IBinder windowToken, boolean setVisible,
-                IVoidResultCallback resultCallback) {
-            CallbackUtils.onResult(resultCallback,
-                    () -> mImms.applyImeVisibility(mToken, windowToken, setVisible));
+        public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible) {
+            mImms.applyImeVisibility(mToken, windowToken, setVisible);
         }
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 90694d0..3f2b8ff 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -224,6 +225,12 @@
         }
 
         public boolean serverBasedResumeOnReboot() {
+            // Always use the server based RoR if the HAL isn't installed on device.
+            if (!mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_REBOOT_ESCROW)) {
+                return true;
+            }
+
             return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
                     "server_based_ror_enabled", false);
         }
@@ -374,6 +381,7 @@
         try {
             escrowKey = getAndClearRebootEscrowKey(kk);
         } catch (IOException e) {
+            Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
             scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
                     rebootEscrowUsers);
             return;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e7c0a50..431b009 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -382,8 +382,8 @@
 
         // Migrate data usage over a VPN to the TUN network.
         for (UnderlyingNetworkInfo info : vpnArray) {
-            delta.migrateTun(info.getOwnerUid(), info.getIface(),
-                    info.getUnderlyingIfaces());
+            delta.migrateTun(info.getOwnerUid(), info.getInterface(),
+                    info.getUnderlyingInterfaces());
             // Filter out debug entries as that may lead to over counting.
             delta.filterDebugEntries();
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d6c145..9a3fe82 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4883,6 +4883,11 @@
                 return super.filterAppAccess(packageName, callingUid, userId);
             }
         }
+        public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+            synchronized (mLock) {
+                super.dump(type, fd, pw, dumpState);
+            }
+        }
     }
 
 
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 622b758..3a097a7 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -280,14 +280,15 @@
     private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
             Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
             ArraySet<String>>> datastore) {
+        final int appId = UserHandle.getAppId(uid);
         // We make a copy of the per UID state to limit our mutation to one
         // operation in the underlying concurrent data structure.
-        ArrayMap<String, ArraySet<String>> uidTags = datastore.get(uid);
-        if (uidTags != null) {
-            uidTags = new ArrayMap<>(uidTags);
+        ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
+        if (appIdTags != null) {
+            appIdTags = new ArrayMap<>(appIdTags);
         }
 
-        ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+        ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
         if (packageTags != null) {
             packageTags = new ArraySet<>(packageTags);
         }
@@ -299,17 +300,17 @@
             } else {
                 packageTags = new ArraySet<>(allowListedTags);
             }
-            if (uidTags == null) {
-                uidTags = new ArrayMap<>();
+            if (appIdTags == null) {
+                appIdTags = new ArrayMap<>();
             }
-            uidTags.put(packageName, packageTags);
-            datastore.put(uid, uidTags);
-        } else if (uidTags != null) {
-            uidTags.remove(packageName);
-            if (!uidTags.isEmpty()) {
-                datastore.put(uid, uidTags);
+            appIdTags.put(packageName, packageTags);
+            datastore.put(appId, appIdTags);
+        } else if (appIdTags != null) {
+            appIdTags.remove(packageName);
+            if (!appIdTags.isEmpty()) {
+                datastore.put(appId, appIdTags);
             } else {
-                datastore.remove(uid);
+                datastore.remove(appId);
             }
         }
     }
@@ -318,9 +319,10 @@
             @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
             ArraySet<String>>> mappedOps) {
         // Only a single lookup from the underlying concurrent data structure
-        final ArrayMap<String, ArraySet<String>> uidTags = mappedOps.get(uid);
-        if (uidTags != null) {
-            final ArraySet<String> packageTags = uidTags.get(packageName);
+        final int appId = UserHandle.getAppId(uid);
+        final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
+        if (appIdTags != null) {
+            final ArraySet<String> packageTags = appIdTags.get(packageName);
             if (packageTags != null && packageTags.contains(attributionTag)) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 08a1b7e..65b947c 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1851,7 +1851,7 @@
 
         private long getNextRetryIntervalsMs() {
             final int retryDelayIndex = mFailedAttempts - 1;
-            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+            final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis();
 
             // Repeatedly use last item in retry timeout list.
             if (retryDelayIndex >= retryIntervalsMs.length) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 65965ad..6198573 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3340,11 +3340,6 @@
     }
 
     @Override
-    public boolean supportsNonResizableMultiWindow() {
-        return mSupportsNonResizableMultiWindow;
-    }
-
-    @Override
     public boolean updateConfiguration(Configuration values) {
         mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
 
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 43326df..d5a7619 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1691,7 +1691,11 @@
 
     static boolean isTaskTransitOld(@TransitionOldType int transit) {
         return isTaskOpenTransitOld(transit)
-                || transit == TRANSIT_OLD_TASK_CLOSE
+                || isTaskCloseTransitOld(transit);
+    }
+
+    static boolean isTaskCloseTransitOld(@TransitionOldType int transit) {
+        return transit == TRANSIT_OLD_TASK_CLOSE
                 || transit == TRANSIT_OLD_TASK_TO_BACK;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e28ab26..a108478 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1538,6 +1538,11 @@
             // to cover the activity configuration change.
             return false;
         }
+        if (r.mStartingData != null && r.mStartingData.hasImeSurface()) {
+            // Currently it is unknown that when will IME window be ready. Reject the case to
+            // avoid flickering by showing IME in inconsistent orientation.
+            return false;
+        }
         if (checkOpening) {
             if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
                 // Apply normal rotation animation in case of the activity set different requested
@@ -2108,9 +2113,7 @@
             }
 
             // Check if input device can dispatch events to current display.
-            // If display type is virtual, will follow the default display.
-            if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(),
-                    displayInfo.type == Display.TYPE_VIRTUAL ? DEFAULT_DISPLAY : mDisplayId)) {
+            if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index af0c3e3..94d81fb 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -129,10 +129,10 @@
     private boolean mParsedXml;
 
     /**
-     * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
-     * than once per throttleTime
+     * Specified duration between requests to generate a display hash in milliseconds. Requests
+     * faster than this delay will be throttled.
      */
-    private int mThrottleDurationMillis = 0;
+    private int mDurationBetweenRequestMillis = 0;
 
     /**
      * The last time an app requested to generate a display hash in System time.
@@ -203,8 +203,8 @@
             return true;
         }
 
-        int throttleDurationMs = getThrottleDurationMillis();
-        if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+        int mDurationBetweenRequestsMs = getDurationBetweenRequestMillis();
+        if (currentTime - mLastRequestTimeMs < mDurationBetweenRequestsMs) {
             return false;
         }
 
@@ -233,7 +233,7 @@
                     (float) size.getHeight() / boundsInWindow.height());
         }
 
-        args.setGrayscale(displayHashParams.isUseGrayscale());
+        args.setGrayscale(displayHashParams.isGrayscaleBuffer());
 
         SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
                 SurfaceControl.captureLayers(args.build());
@@ -356,11 +356,11 @@
         }
     }
 
-    private int getThrottleDurationMillis() {
+    private int getDurationBetweenRequestMillis() {
         if (!parseXmlProperties()) {
             return 0;
         }
-        return mThrottleDurationMillis;
+        return mDurationBetweenRequestMillis;
     }
 
     private boolean parseXmlProperties() {
@@ -406,8 +406,8 @@
             }
 
             TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHashingService);
-            mThrottleDurationMillis = sa.getInt(
-                    R.styleable.DisplayHashingService_throttleDurationMillis, 0);
+            mDurationBetweenRequestMillis = sa.getInt(
+                    R.styleable.DisplayHashingService_durationBetweenRequestsMillis, 0);
             sa.recycle();
             mParsedXml = true;
             return true;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 84616c0..aa7e6c9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -116,10 +116,8 @@
     /** Notifies that the input device configuration has changed. */
     @Override
     public void notifyConfigurationChanged() {
-        // TODO(multi-display): Notify proper displays that are associated with this input device.
-
         synchronized (mService.mGlobalLock) {
-            mService.getDefaultDisplayContentLocked().sendNewConfiguration();
+            mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
         }
 
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d9c5fa4..dc07988 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2657,9 +2657,12 @@
     }
 
     void addStartingWindowsForVisibleActivities() {
+        final ArrayList<Task> addedTasks = new ArrayList<>();
         forAllActivities((r) -> {
-            if (r.mVisibleRequested) {
+            final Task task = r.getTask();
+            if (r.mVisibleRequested && r.mStartingData == null && !addedTasks.contains(task)) {
                 r.showStartingWindow(true /*taskSwitch*/);
+                addedTasks.add(task);
             }
         });
     }
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 2124ed6..66ae0eb 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -39,4 +39,9 @@
         return mService.mStartingSurfaceController.createTaskSnapshotSurface(activity,
                 mSnapshot);
     }
+
+    @Override
+    boolean hasImeSurface() {
+        return mSnapshot.hasImeSurface();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index a5bd797..59de43a 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -40,4 +40,9 @@
      *         {@link StartingSurface#remove}
      */
     abstract StartingSurface createStartingSurface(ActivityRecord activity);
+
+    /** @see android.window.TaskSnapshot#hasImeSurface() */
+    boolean hasImeSurface() {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a9b06ca..c3815c1 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -118,7 +118,9 @@
                 return null;
             }
             if (topFullscreenActivity.getWindowConfiguration().getRotation()
-                    != taskSnapshot.getRotation()) {
+                    != taskSnapshot.getRotation()
+                    // Use normal rotation to avoid flickering of IME window in old orientation.
+                    && !taskSnapshot.hasImeSurface()) {
                 // The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
                 // that the activity will be updated to the same rotation as the snapshot. Since
                 // the transition is not started yet, fixed rotation transform needs to be applied
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2a0041a..9fad7da 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4079,6 +4079,7 @@
         info.lastActiveTime = lastActiveTime;
         info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
         info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
+        info.supportsMultiWindow = supportsMultiWindow();
         info.configuration.setTo(getConfiguration());
         // Update to the task's current activity type and windowing mode which may differ from the
         // window configuration
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index cda8c4b..87f685e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1274,7 +1274,15 @@
 
         for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
             if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
-                return mLaunchRootTasks.get(i).task;
+                final Task launchRootTask = mLaunchRootTasks.get(i).task;
+                // Return the focusable root task for improving the UX with staged split screen.
+                final Task adjacentRootTask = launchRootTask != null
+                        ? launchRootTask.mAdjacentTask : null;
+                if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
+                    return adjacentRootTask;
+                } else {
+                    return launchRootTask;
+                }
             }
         }
         return null;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bea733b..b1c7e19 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -37,6 +37,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -2696,7 +2697,14 @@
             @Nullable ArrayList<WindowContainer> sources) {
         final Task task = asTask();
         if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
-            mDisplayContent.showImeScreenshot();
+            final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
+            final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
+                    && imeTarget.getWindow().getTask() == task;
+            // Attach and show the IME screenshot when the task is the IME target and performing
+            // task closing transition to the next task.
+            if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
+                mDisplayContent.showImeScreenshot();
+            }
         }
         final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                 transit, enter, isVoiceInteraction);
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index d076434..51bc99a 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -27,3 +27,4 @@
 per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
 per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
 per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2a0feaf..24f73c3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -883,6 +883,12 @@
                 synchronized (getLockObject()) {
                     // Check whether the user is affiliated, *before* removing its data.
                     boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
+                    if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+                        // Disable network and security logging
+                        mInjector.securityLogSetLoggingEnabledProperty(false);
+                        mSecurityLogMonitor.stop();
+                        setNetworkLoggingActiveInternal(false);
+                    }
                     removeUserData(userHandle);
                     if (!isRemovedUserAffiliated) {
                         // We discard the logs when unaffiliated users are deleted (so that the
@@ -8552,11 +8558,12 @@
         synchronized (getLockObject()) {
             enforceCanSetProfileOwnerLocked(
                     caller, who, userHandle, hasIncompatibleAccountsOrNonAdb);
-            Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle),
-                    "Component " + who + " not installed for userId:" + userHandle);
             final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
-            Preconditions.checkArgument(admin != null && !getUserData(
-                    userHandle).mRemovingAdmins.contains(who), "Not active admin: " + who);
+            Preconditions.checkArgument(
+                    isPackageInstalledForUser(who.getPackageName(), userHandle)
+                            && admin != null
+                            && !getUserData(userHandle).mRemovingAdmins.contains(who),
+                    "Not active admin: " + who);
 
             final int parentUserId = getProfileParentId(userHandle);
             // When trying to set a profile owner on a new user, it may be that this user is
@@ -8896,17 +8903,17 @@
     }
 
     @Override
-    public ComponentName getProfileOwnerAsUser(int userHandle) {
+    public ComponentName getProfileOwnerAsUser(int userId) {
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
 
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userHandle));
+        CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
 
         synchronized (getLockObject()) {
-            return mOwners.getProfileOwnerComponent(userHandle);
+            return mOwners.getProfileOwnerComponent(userId);
         }
     }
 
@@ -9350,19 +9357,20 @@
     public List<UserHandle> listForegroundAffiliatedUsers() {
         checkIsDeviceOwner(getCallerIdentity());
 
-        int userId = mInjector.binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId());
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            int userId = getCurrentForegroundUserId();
+            boolean isAffiliated;
+            synchronized (getLockObject()) {
+                isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
+            }
 
-        boolean isAffiliated;
-        synchronized (getLockObject()) {
-            isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
-        }
+            if (!isAffiliated) return Collections.emptyList();
 
-        if (!isAffiliated) return Collections.emptyList();
+            List<UserHandle> users = new ArrayList<>(1);
+            users.add(UserHandle.of(userId));
 
-        List<UserHandle> users = new ArrayList<>(1);
-        users.add(UserHandle.of(userId));
-
-        return users;
+            return users;
+        });
     }
 
     protected int getProfileParentId(int userHandle) {
@@ -13319,12 +13327,10 @@
         final CallerIdentity caller = getCallerIdentity();
         final long ident = mInjector.binderClearCallingIdentity();
         try {
-            final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser(
-                    packageName, caller.getUserId());
-            Preconditions.checkArgument(caller.getUid() == uidForPackage,
+            final List<String> callerUidPackageNames = Arrays.asList(
+                    mInjector.getPackageManager().getPackagesForUid(caller.getUid()));
+            Preconditions.checkArgument(callerUidPackageNames.contains(packageName),
                     "Caller uid doesn't match the one for the provided package.");
-        } catch (NameNotFoundException e) {
-            throw new IllegalArgumentException("Invalid package provided " + packageName, e);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -14124,7 +14130,7 @@
         }
     }
 
-    private boolean isUserAffiliatedWithDeviceLocked(int userId) {
+    private boolean isUserAffiliatedWithDeviceLocked(@UserIdInt int userId) {
         if (!mOwners.hasDeviceOwner()) {
             return false;
         }
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 75432cd..9706d7f 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -29,6 +29,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UpdateEngine;
 import android.os.UpdateEngineCallback;
 import android.os.UserHandle;
@@ -79,7 +80,7 @@
      */
     public static boolean enabled() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, "enabled",
-            false);
+            false) || SystemProperties.getBoolean("persist.profcollectd.enabled_override", false);
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 3bc1c8a..64dad7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -80,6 +80,7 @@
 import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
 import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
 import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
 import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
 import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
 import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
@@ -771,10 +772,10 @@
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6);
         assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
 
-        mService.removeLocked(pi6, null);
+        mService.removeLocked(pi6, null, REMOVE_REASON_UNDEFINED);
         assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
 
-        mService.removeLocked(pi8, null);
+        mService.removeLocked(pi8, null, REMOVE_REASON_UNDEFINED);
         assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
     }
 
@@ -1252,7 +1253,8 @@
             setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
         }
         assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
-        mService.removeLocked(TEST_CALLING_UID);
+        mService.removeLocked(TEST_CALLING_UID,
+                REMOVE_REASON_UNDEFINED);
         assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
     }
 
@@ -1292,7 +1294,7 @@
         }
         assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
         for (int i = 0; i < numAlarms; i++) {
-            mService.removeLocked(pis[i], null);
+            mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
             assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
         }
     }
@@ -1422,7 +1424,7 @@
         mTestTimer.expire();
         assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
         for (int i = 0; i < numAlarms; i++) {
-            mService.removeLocked(pis[i], null);
+            mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
             assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
         }
     }
@@ -1492,13 +1494,13 @@
         assertEquals(trigger6, mTestTimer.getElapsed());
         assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle6, null);
+        mService.removeLocked(wakeFromIdle6, null, REMOVE_REASON_UNDEFINED);
 
         assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
         assertEquals(trigger10, mTestTimer.getElapsed());
         assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle10, null);
+        mService.removeLocked(wakeFromIdle10, null, REMOVE_REASON_UNDEFINED);
         assertNull(mService.mNextWakeFromIdle);
     }
 
@@ -1524,13 +1526,13 @@
         setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
         assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle5, null);
+        mService.removeLocked(wakeFromIdle5, null, REMOVE_REASON_UNDEFINED);
         assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());
 
-        mService.removeLocked(wakeFromIdle8, null);
+        mService.removeLocked(wakeFromIdle8, null, REMOVE_REASON_UNDEFINED);
         assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
 
-        mService.removeLocked(idleUntilPi, null);
+        mService.removeLocked(idleUntilPi, null, REMOVE_REASON_UNDEFINED);
         assertNull(mService.mPendingIdleUntil);
 
         setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
@@ -1641,7 +1643,7 @@
         verify(pis[0], never()).send(eq(mMockContext), eq(0), any(Intent.class), any(),
                 any(Handler.class), isNull(), any());
 
-        mService.removeLocked(idleUntil, null);
+        mService.removeLocked(idleUntil, null, REMOVE_REASON_UNDEFINED);
         mTestTimer.expire();
         // Now, the first 5 alarms (upto i = 4) should expire.
         for (int i = 0; i < 5; i++) {
@@ -1688,14 +1690,16 @@
                 getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
 
         // Refresh the state
-        mService.removeLocked(TEST_CALLING_UID);
+        mService.removeLocked(TEST_CALLING_UID,
+                REMOVE_REASON_UNDEFINED);
         mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
 
         testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
                 trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
 
         // Refresh the state
-        mService.removeLocked(TEST_CALLING_UID);
+        mService.removeLocked(TEST_CALLING_UID,
+                REMOVE_REASON_UNDEFINED);
         mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
 
         testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 45f43e8..ee00cb2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -43,9 +43,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SigningInfo;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -81,8 +84,10 @@
     private static final int APP_PID = 2000;
     private static final int SYSTEM_PID = 558;
     private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+    private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility";
     private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
-            "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
+            TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest");
+    private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package";
 
     private static final int[] ALWAYS_DISPATCH_EVENTS = {
             AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -142,19 +147,40 @@
     @Mock
     private AccessibilityServiceInfo mMockA11yServiceInfo;
     @Mock
+    private ResolveInfo mMockResolveInfo;
+    @Mock
+    private ServiceInfo mMockServiceInfo;
+    @Mock
+    private ApplicationInfo mMockApplicationInfo;
+    @Mock
+    private ApplicationInfo mMockSourceApplicationInfo;
+    @Mock
+    private PackageInfo mMockSourcePackageInfo;
+    @Mock
     private PolicyWarningUIController mPolicyWarningUIController;
 
     @Before
-    public void setUp() {
+    public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
         mContext.setMockPackageManager(mMockPackageManager);
         mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
         mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
         mContext.getOrCreateTestableResources().addOverride(
                 R.dimen.accessibility_focus_highlight_stroke_width, 1);
+        mContext.getOrCreateTestableResources().addOverride(R.array
+                        .config_accessibility_allowed_install_source,
+                new String[]{ALLOWED_INSTALL_PACKAGE_NAME});
 
+        when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
         when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
         when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+        when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn(
+                mMockSourcePackageInfo);
+
+        mMockResolveInfo.serviceInfo = mMockServiceInfo;
+        mMockServiceInfo.applicationInfo = mMockApplicationInfo;
+        mMockServiceInfo.packageName = TEST_PACKAGE_NAME;
+        mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
 
         mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
                 mPolicyWarningUIController, mContext, mMockA11yUserManager);
@@ -595,28 +621,7 @@
     }
 
     @Test
-    public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() {
-        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
-        boundServices.add(mMockA11yServiceConnection);
-        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
-
-        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
-        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
-    }
-
-    @Test
-    public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() {
-        onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction();
-
-        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
-        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
-                any());
-    }
-
-    @Test
-    public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() {
+    public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() {
         final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
         boundServices.add(mMockA11yServiceConnection);
         when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
@@ -628,8 +633,8 @@
     }
 
     @Test
-    public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() {
-        onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+    public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() {
+        onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
 
         mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
 
@@ -638,8 +643,65 @@
     }
 
     @Test
+    public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() {
+        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+        boundServices.add(mMockA11yServiceConnection);
+        when(mMockApplicationInfo.isSystemApp()).thenReturn(true);
+        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+    }
+
+    @Test
+    public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() {
+        onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction();
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+                any());
+    }
+
+    @Test
+    public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction()
+            throws PackageManager.NameNotFoundException {
+        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+        boundServices.add(mMockA11yServiceConnection);
+        when(mMockApplicationInfo.isSystemApp()).thenReturn(false);
+        final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
+                ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null,
+                ALLOWED_INSTALL_PACKAGE_NAME);
+        when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+                installSourceInfo);
+        when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true);
+        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+        verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+    }
+
+    @Test
+    public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction()
+            throws PackageManager.NameNotFoundException {
+        final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+        boundServices.add(mMockA11yServiceConnection);
+        when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+        final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null);
+        when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+                installSourceInfo);
+
+        mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+        verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+                eq(TEST_COMPONENT_NAME));
+    }
+
+    @Test
     public void onSwitchUser_differentUser_activateUIControllerAction() {
-        onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+        onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
 
         mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 81570a1..fe0df58 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -253,6 +253,8 @@
 
         doReturn(new String[] {admin.getPackageName()}).when(mServices.ipackageManager)
             .getPackagesForUid(eq(packageUid));
+        doReturn(new String[] {admin.getPackageName()}).when(mServices.packageManager)
+                .getPackagesForUid(eq(packageUid));
         // Set up getPackageInfo().
         markPackageAsInstalled(admin.getPackageName(), ai, UserHandle.getUserId(packageUid));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e1eef76..e09606e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -54,6 +54,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1909,6 +1912,32 @@
         verify(t).show(mDisplayContent.mImeScreenshot);
     }
 
+    @UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
+    @Test
+    public void testShowImeScreenshot() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+        task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+        doReturn(true).when(task).okToAnimate();
+        ArrayList<WindowContainer> sources = new ArrayList<>();
+        sources.add(activity);
+
+        mDisplayContent.setImeLayeringTarget(win);
+        spyOn(mDisplayContent);
+
+        // Expecting the IME screenshot only be attached when performing task closing transition.
+        task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+                false /* isVoiceInteraction */, sources);
+        verify(mDisplayContent).showImeScreenshot();
+
+        clearInvocations(mDisplayContent);
+        activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
+                false /* isVoiceInteraction */, sources);
+        verify(mDisplayContent, never()).showImeScreenshot();
+    }
+
     @Test
     public void testRotateBounds_keepSamePhysicalPosition() {
         final DisplayContent dc =
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 9289ce4..67b273a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -101,6 +101,29 @@
     }
 
     @Test
+    public void getLaunchRootTask_checksFocusedRootTask() {
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task rootTask = createTaskWithActivity(
+                taskDisplayArea,
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, ON_TOP, true);
+        rootTask.mCreatedByOrganizer = true;
+
+        final Task adjacentRootTask = createTask(
+                mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+        adjacentRootTask.mCreatedByOrganizer = true;
+        adjacentRootTask.mAdjacentTask = rootTask;
+        rootTask.mAdjacentTask = adjacentRootTask;
+
+        taskDisplayArea.setLaunchRootTask(rootTask,
+                new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+        Task actualRootTask = taskDisplayArea.getLaunchRootTask(
+                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, null /* options */,
+                null /* sourceTask */, 0 /*launchFlags*/);
+        assertTrue(actualRootTask.isFocusedRootTaskOnDisplay());
+    }
+
+    @Test
     public void getLaunchRootTask_fromLaunchAdjacentFlagRoot_checksAdjacentRoot() {
         final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
         final Task rootTask = createTask(
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index d8bd6a5..c5fc436 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -3448,4 +3448,13 @@
     public Handler getHandler() {
         return mHandler;
     }
+
+    /**
+     * Sets this {@link ConnectionService} ready for testing purposes.
+     * @hide
+     */
+    @VisibleForTesting
+    public void setReadyForTest() {
+        mAreAccountsInitialized = true;
+    }
 }
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 3b28616..8df41fb 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -183,16 +183,6 @@
     }
 
     /**
-     * @return the absolute radio frequency channel number for this physical channel,
-     * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
-     * @deprecated Use {@link #getDownlinkChannelNumber()} to get the channel number.
-     */
-    @Deprecated
-    public int getChannelNumber() {
-        return getDownlinkChannelNumber();
-    }
-
-    /**
      * @return the rough frequency range for this physical channel,
      * {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown.
      * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 05f5d29..179aead 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15036,6 +15036,15 @@
             "CAPABILITY_SLICING_CONFIG_SUPPORTED";
 
     /**
+     * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on
+     * respective methods for more information.
+     *
+     * @hide
+     */
+    public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED =
+            "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
+
+    /**
      * A list of the radio interface capability values with public valid constants.
      *
      * Here is a related list for the systemapi-only valid constants:
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
index 87cfb34..f23ba26 100644
--- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
+++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -36,15 +36,15 @@
     @Test
     fun testParcelUnparcel() {
         val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
-        assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
-        assertEquals(TEST_IFACE, testInfo.iface)
-        assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+        assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid())
+        assertEquals(TEST_IFACE, testInfo.getInterface())
+        assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces())
         assertParcelSane(testInfo, 3)
 
         val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
-        assertEquals(0, emptyInfo.ownerUid)
-        assertEquals(String(), emptyInfo.iface)
-        assertEquals(listOf(), emptyInfo.underlyingIfaces)
+        assertEquals(0, emptyInfo.getOwnerUid())
+        assertEquals(String(), emptyInfo.getInterface())
+        assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces())
         assertParcelSane(emptyInfo, 3)
     }
 }
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d966f70..f277e94 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5898,9 +5898,9 @@
             assertEquals("Should have exactly one VPN:", 1, infos.length);
             UnderlyingNetworkInfo info = infos[0];
             assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid());
-            assertEquals("Unexpected VPN interface:", vpnIfname, info.getIface());
+            assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface());
             assertSameElementsNoDuplicates(underlyingIfaces,
-                    info.getUnderlyingIfaces().toArray(new String[0]));
+                    info.getUnderlyingInterfaces().toArray(new String[0]));
         } else {
             assertEquals(0, infos.length);
             return;
@@ -6044,8 +6044,8 @@
         // network for the VPN...
         verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
                 any(List.class), any() /* anyString() doesn't match null */,
-                argThat(infos -> infos.get(0).getUnderlyingIfaces().size() == 1
-                        && WIFI_IFNAME.equals(infos.get(0).getUnderlyingIfaces().get(0))));
+                argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1
+                        && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0))));
         verifyNoMoreInteractions(mStatsManager);
         reset(mStatsManager);
 
@@ -6059,8 +6059,8 @@
         waitForIdle();
         verify(mStatsManager).notifyNetworkStatus(any(List.class),
                 any(List.class), any() /* anyString() doesn't match null */,
-                argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingIfaces().size() == 1
-                        && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingIfaces().get(0))));
+                argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1
+                        && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0))));
         mEthernetNetworkAgent.disconnect();
         waitForIdle();
         reset(mStatsManager);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 7cfd275..9410886 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -82,7 +82,7 @@
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
         final VcnGatewayConnectionConfig.Builder builder =
-                newBuilder().setRetryIntervalsMs(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+                newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
 
         for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
@@ -134,7 +134,7 @@
     @Test
     public void testBuilderRequiresNonNullRetryInterval() {
         try {
-            newBuilder().setRetryIntervalsMs(null);
+            newBuilder().setRetryIntervalsMillis(null);
             fail("Expected exception due to invalid retryIntervalMs");
         } catch (IllegalArgumentException e) {
         }
@@ -143,7 +143,7 @@
     @Test
     public void testBuilderRequiresNonEmptyRetryInterval() {
         try {
-            newBuilder().setRetryIntervalsMs(new long[0]);
+            newBuilder().setRetryIntervalsMillis(new long[0]);
             fail("Expected exception due to invalid retryIntervalMs");
         } catch (IllegalArgumentException e) {
         }
@@ -174,7 +174,7 @@
 
         assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
 
-        assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+        assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
         assertEquals(MAX_MTU, config.getMaxMtu());
     }
 
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 044bef5..a88f112 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -38,7 +38,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mFirstRetryInterval = mConfig.getRetryIntervalsMs()[0];
+        mFirstRetryInterval = mConfig.getRetryIntervalsMillis()[0];
 
         mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
         mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b78f48c..6364ccd 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -78,6 +78,8 @@
       return "interpolator";
     case ResourceType::kLayout:
       return "layout";
+    case ResourceType::kMacro:
+      return "macro";
     case ResourceType::kMenu:
       return "menu";
     case ResourceType::kMipmap:
@@ -119,6 +121,7 @@
     {"integer", ResourceType::kInteger},
     {"interpolator", ResourceType::kInterpolator},
     {"layout", ResourceType::kLayout},
+    {"macro", ResourceType::kMacro},
     {"menu", ResourceType::kMenu},
     {"mipmap", ResourceType::kMipmap},
     {"navigation", ResourceType::kNavigation},
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cf93870..307c21d 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -57,6 +57,7 @@
   kInteger,
   kInterpolator,
   kLayout,
+  kMacro,
   kMenu,
   kMipmap,
   kNavigation,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 24c60b7..1efabbb 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -627,6 +627,16 @@
     }
 
     return true;
+  } else if (resource_type == "macro") {
+    if (!maybe_name) {
+      diag_->Error(DiagMessage(out_resource->source)
+                   << "<" << parser->element_name() << "> missing 'name' attribute");
+      return false;
+    }
+
+    out_resource->name.type = ResourceType::kMacro;
+    out_resource->name.entry = maybe_name.value().to_string();
+    return ParseMacro(parser, out_resource);
   }
 
   if (can_be_item) {
@@ -726,16 +736,8 @@
   return true;
 }
 
-/**
- * Reads the entire XML subtree and attempts to parse it as some Item,
- * with typeMask denoting which items it can be. If allowRawValue is
- * true, a RawString is returned if the XML couldn't be parsed as
- * an Item. If allowRawValue is false, nullptr is returned in this
- * case.
- */
-std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
-                                               const uint32_t type_mask,
-                                               const bool allow_raw_value) {
+std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree(
+    xml::XmlPullParser* parser) {
   const size_t begin_xml_line = parser->line_number();
 
   std::string raw_value;
@@ -745,30 +747,60 @@
     return {};
   }
 
-  if (!style_string.spans.empty()) {
+  return FlattenedXmlSubTree{.raw_value = raw_value,
+                             .style_string = style_string,
+                             .untranslatable_sections = untranslatable_sections,
+                             .namespace_resolver = parser,
+                             .source = source_.WithLine(begin_xml_line)};
+}
+
+/**
+ * Reads the entire XML subtree and attempts to parse it as some Item,
+ * with typeMask denoting which items it can be. If allowRawValue is
+ * true, a RawString is returned if the XML couldn't be parsed as
+ * an Item. If allowRawValue is false, nullptr is returned in this
+ * case.
+ */
+std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
+                                               const bool allow_raw_value) {
+  auto sub_tree = CreateFlattenSubTree(parser);
+  if (!sub_tree.has_value()) {
+    return {};
+  }
+  return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_);
+}
+
+std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree,
+                                               const uint32_t type_mask, const bool allow_raw_value,
+                                               ResourceTable& table,
+                                               const android::ConfigDescription& config,
+                                               IDiagnostics& diag) {
+  if (!xmlsub_tree.style_string.spans.empty()) {
     // This can only be a StyledString.
     std::unique_ptr<StyledString> styled_string =
-        util::make_unique<StyledString>(table_->string_pool.MakeRef(
-            style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
-    styled_string->untranslatable_sections = std::move(untranslatable_sections);
+        util::make_unique<StyledString>(table.string_pool.MakeRef(
+            xmlsub_tree.style_string,
+            StringPool::Context(StringPool::Context::kNormalPriority, config)));
+    styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
     return std::move(styled_string);
   }
 
   auto on_create_reference = [&](const ResourceName& name) {
     // name.package can be empty here, as it will assume the package name of the
     // table.
-    std::unique_ptr<Id> id = util::make_unique<Id>();
-    id->SetSource(source_.WithLine(begin_xml_line));
-    table_->AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), diag_);
+    auto id = util::make_unique<Id>();
+    id->SetSource(xmlsub_tree.source);
+    return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag);
   };
 
   // Process the raw value.
-  std::unique_ptr<Item> processed_item =
-      ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
+  std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute(
+      xmlsub_tree.raw_value, type_mask, on_create_reference);
   if (processed_item) {
     // Fix up the reference.
-    if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
-      ResolvePackage(parser, ref);
+    if (auto ref = ValueCast<Reference>(processed_item.get())) {
+      ref->allow_raw = allow_raw_value;
+      ResolvePackage(xmlsub_tree.namespace_resolver, ref);
     }
     return processed_item;
   }
@@ -777,17 +809,16 @@
   if (type_mask & android::ResTable_map::TYPE_STRING) {
     // Use the trimmed, escaped string.
     std::unique_ptr<String> string = util::make_unique<String>(
-        table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
-    string->untranslatable_sections = std::move(untranslatable_sections);
+        table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config)));
+    string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
     return std::move(string);
   }
 
   if (allow_raw_value) {
     // We can't parse this so return a RawString if we are allowed.
-    return util::make_unique<RawString>(
-        table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
-                                    StringPool::Context(config_)));
-  } else if (util::TrimWhitespace(raw_value).empty()) {
+    return util::make_unique<RawString>(table.string_pool.MakeRef(
+        util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config)));
+  } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) {
     // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
     return ResourceUtils::MakeNull();
   }
@@ -850,6 +881,35 @@
   return true;
 }
 
+bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+  auto sub_tree = CreateFlattenSubTree(parser);
+  if (!sub_tree) {
+    return false;
+  }
+
+  if (out_resource->config != ConfigDescription::DefaultConfig()) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "<macro> tags cannot be declared in configurations other than the default "
+                    "configuration'");
+    return false;
+  }
+
+  auto macro = std::make_unique<Macro>();
+  macro->raw_value = std::move(sub_tree->raw_value);
+  macro->style_string = std::move(sub_tree->style_string);
+  macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections);
+
+  for (const auto& decl : parser->package_decls()) {
+    macro->alias_namespaces.emplace_back(
+        Macro::Namespace{.alias = decl.prefix,
+                         .package_name = decl.package.package,
+                         .is_private = decl.package.private_namespace});
+  }
+
+  out_resource->value = std::move(macro);
+  return true;
+}
+
 bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (options_.visibility) {
     diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index af0db8c..5c92def 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,14 @@
   Maybe<Visibility::Level> visibility;
 };
 
+struct FlattenedXmlSubTree {
+  std::string raw_value;
+  StyleString style_string;
+  std::vector<UntranslatableSection> untranslatable_sections;
+  xml::IPackageDeclStack* namespace_resolver;
+  Source source;
+};
+
 /*
  * Parses an XML file for resources and adds them to a ResourceTable.
  */
@@ -67,9 +75,16 @@
                  const ResourceParserOptions& options = {});
   bool Parse(xml::XmlPullParser* parser);
 
+  static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask,
+                                        bool allow_raw_value, ResourceTable& table,
+                                        const android::ConfigDescription& config,
+                                        IDiagnostics& diag);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceParser);
 
+  std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
+
   // Parses the XML subtree as a StyleString (flattened XML representation for strings with
   // formatting). If parsing fails, false is returned and the out parameters are left in an
   // unspecified state. Otherwise,
@@ -96,7 +111,7 @@
 
   bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format);
   bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
-
+  bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
@@ -108,8 +123,7 @@
   bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
   Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
                                                const android::StringPiece& tag);
-  bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
-                  ParsedResource* out_resource);
+  bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
   bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4a509be..279ebcba 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -336,6 +336,90 @@
   EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
 }
 
+TEST_F(ResourceParserTest, ParseMacro) {
+  std::string input = R"(<macro name="foo">12345</macro>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->raw_value, Eq("12345"));
+  EXPECT_THAT(macro->style_string.str, Eq("12345"));
+  EXPECT_THAT(macro->style_string.spans, IsEmpty());
+  EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+  EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) {
+  std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house."));
+  EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house."));
+  EXPECT_THAT(macro->style_string.spans.size(), Eq(1));
+  EXPECT_THAT(macro->style_string.spans[0].name, Eq("b"));
+  EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12));
+  EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16));
+  ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1));
+  EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12));
+  EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17));
+  EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroNamespaces) {
+  std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android">
+@app:string/foo</macro>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo"));
+  EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo"));
+  EXPECT_THAT(macro->style_string.spans, IsEmpty());
+  EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+  EXPECT_THAT(macro->alias_namespaces.size(), Eq(1));
+  EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app"));
+  EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android"));
+  EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false));
+}
+
+TEST_F(ResourceParserTest, ParseMacroReference) {
+  std::string input = R"(<string name="res_string">@macro/foo</string>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Reference* macro = test::GetValue<Reference>(&table_, "string/res_string");
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING));
+  EXPECT_THAT(macro->allow_raw, Eq(false));
+
+  input = R"(<style name="foo">
+               <item name="bar">@macro/foo</item>
+             </style>)";
+
+  ASSERT_TRUE(TestParse(input));
+  Style* style = test::GetValue<Style>(&table_, "style/foo");
+  ASSERT_THAT(style, NotNull());
+  EXPECT_THAT(style->entries.size(), Eq(1));
+
+  macro = ValueCast<Reference>(style->entries[0].value.get());
+  ASSERT_THAT(macro, NotNull());
+  EXPECT_THAT(macro->type_flags, Eq(0U));
+  EXPECT_THAT(macro->allow_raw, Eq(true));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNoNameFail) {
+  std::string input = R"(<macro>12345</macro>)";
+  ASSERT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) {
+  const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
+  std::string input = R"(<macro name="foo">12345</macro>)";
+  ASSERT_FALSE(TestParse(input, watch_config));
+}
+
 // Old AAPT allowed attributes to be defined under different configurations, but ultimately
 // stored them with the default configuration. Check that we have the same behavior.
 TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 5b43df6..e0e80ac 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -628,7 +628,7 @@
 
 std::unique_ptr<Item> TryParseItemForAttribute(
     const StringPiece& value, uint32_t type_mask,
-    const std::function<void(const ResourceName&)>& on_create_reference) {
+    const std::function<bool(const ResourceName&)>& on_create_reference) {
   using android::ResTable_map;
 
   auto null_or_empty = TryParseNullOrEmpty(value);
@@ -639,8 +639,11 @@
   bool create = false;
   auto reference = TryParseReference(value, &create);
   if (reference) {
+    reference->type_flags = type_mask;
     if (create && on_create_reference) {
-      on_create_reference(reference->name.value());
+      if (!on_create_reference(reference->name.value())) {
+        return {};
+      }
     }
     return std::move(reference);
   }
@@ -689,7 +692,7 @@
  */
 std::unique_ptr<Item> TryParseItemForAttribute(
     const StringPiece& str, const Attribute* attr,
-    const std::function<void(const ResourceName&)>& on_create_reference) {
+    const std::function<bool(const ResourceName&)>& on_create_reference) {
   using android::ResTable_map;
 
   const uint32_t type_mask = attr->type_mask;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f77766e..be493db 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -204,11 +204,11 @@
  */
 std::unique_ptr<Item> TryParseItemForAttribute(
     const android::StringPiece& value, const Attribute* attr,
-    const std::function<void(const ResourceName&)>& on_create_reference = {});
+    const std::function<bool(const ResourceName&)>& on_create_reference = {});
 
 std::unique_ptr<Item> TryParseItemForAttribute(
     const android::StringPiece& value, uint32_t type_mask,
-    const std::function<void(const ResourceName&)>& on_create_reference = {});
+    const std::function<bool(const ResourceName&)>& on_create_reference = {});
 
 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
 
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 574bd2e..2a90f26 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -111,12 +111,15 @@
   if (!other) {
     return false;
   }
-  return reference_type == other->reference_type &&
-         private_reference == other->private_reference && id == other->id &&
-         name == other->name;
+  return reference_type == other->reference_type && private_reference == other->private_reference &&
+         id == other->id && name == other->name && type_flags == other->type_flags;
 }
 
 bool Reference::Flatten(android::Res_value* out_value) const {
+  if (name && name.value().type == ResourceType::kMacro) {
+    return false;
+  }
+
   const ResourceId resid = id.value_or_default(ResourceId(0));
   const bool dynamic = resid.is_valid() && is_dynamic;
 
@@ -551,7 +554,7 @@
   return this_type_mask == that_type_mask;
 }
 
-std::string Attribute::MaskString() const {
+std::string Attribute::MaskString(uint32_t type_mask) {
   if (type_mask == android::ResTable_map::TYPE_ANY) {
     return "any";
   }
@@ -650,6 +653,10 @@
   return out.str();
 }
 
+std::string Attribute::MaskString() const {
+  return MaskString(type_mask);
+}
+
 void Attribute::Print(std::ostream* out) const {
   *out << "(attr) " << MaskString();
 
@@ -1017,6 +1024,21 @@
        << " [" << util::Joiner(entries, ", ") << "]";
 }
 
+bool Macro::Equals(const Value* value) const {
+  const Macro* other = ValueCast<Macro>(value);
+  if (!other) {
+    return false;
+  }
+  return other->raw_value == raw_value && other->style_string.spans == style_string.spans &&
+         other->style_string.str == style_string.str &&
+         other->untranslatable_sections == untranslatable_sections &&
+         other->alias_namespaces == alias_namespaces;
+}
+
+void Macro::Print(std::ostream* out) const {
+  *out << "(macro) ";
+}
+
 bool operator<(const Reference& a, const Reference& b) {
   int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
   if (cmp != 0) return cmp < 0;
@@ -1149,4 +1171,9 @@
   return CopyValueFields(std::move(new_value), value);
 }
 
+std::unique_ptr<Macro> CloningValueTransformer::TransformDerived(const Macro* value) {
+  auto new_value = std::make_unique<Macro>(*value);
+  return CopyValueFields(std::move(new_value), value);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 025864d..d11b013 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -164,6 +164,8 @@
   Reference::Type reference_type;
   bool private_reference = false;
   bool is_dynamic = false;
+  std::optional<uint32_t> type_flags;
+  bool allow_raw;
 
   Reference();
   explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
@@ -311,6 +313,8 @@
   bool IsCompatibleWith(const Attribute& attr) const;
 
   std::string MaskString() const;
+  static std::string MaskString(uint32_t type_mask);
+
   void Print(std::ostream* out) const override;
   bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
 };
@@ -362,6 +366,28 @@
   void MergeWith(Styleable* styleable);
 };
 
+struct Macro : public TransformableValue<Macro, BaseValue<Macro>> {
+  std::string raw_value;
+  StyleString style_string;
+  std::vector<UntranslatableSection> untranslatable_sections;
+
+  struct Namespace {
+    std::string alias;
+    std::string package_name;
+    bool is_private;
+
+    bool operator==(const Namespace& right) const {
+      return alias == right.alias && package_name == right.package_name &&
+             is_private == right.is_private;
+    }
+  };
+
+  std::vector<Namespace> alias_namespaces;
+
+  bool Equals(const Value* value) const override;
+  void Print(std::ostream* out) const override;
+};
+
 template <typename T>
 typename std::enable_if<std::is_base_of<Value, T>::value, std::ostream&>::type operator<<(
     std::ostream& out, const std::unique_ptr<T>& value) {
@@ -388,6 +414,7 @@
   std::unique_ptr<Array> TransformDerived(const Array* value) override;
   std::unique_ptr<Plural> TransformDerived(const Plural* value) override;
   std::unique_ptr<Styleable> TransformDerived(const Styleable* value) override;
+  std::unique_ptr<Macro> TransformDerived(const Macro* value) override;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 4247ec5..b45c040 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -273,6 +273,7 @@
     Styleable styleable = 3;
     Array array = 4;
     Plural plural = 5;
+    MacroBody macro = 6;
   }
 }
 
@@ -304,6 +305,13 @@
 
   // Whether this reference is dynamic.
   Boolean is_dynamic = 5;
+
+  // The type flags used when compiling the reference. Used for substituting the contents of macros.
+  uint32 type_flags = 6;
+
+  // Whether raw string values would have been accepted in place of this reference definition. Used
+  // for substituting the contents of macros.
+  bool allow_raw = 7;
 }
 
 // A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -591,3 +599,32 @@
   // The optional interpreted/compiled version of the `value` string.
   Item compiled_item = 6;
 }
+
+message MacroBody {
+  string raw_string = 1;
+  StyleString style_string = 2;
+  repeated UntranslatableSection untranslatable_sections = 3;
+  repeated NamespaceAlias namespace_stack = 4;
+  SourcePosition source = 5;
+}
+
+message NamespaceAlias {
+  string prefix = 1;
+  string package_name = 2;
+  bool is_private = 3;
+}
+
+message StyleString {
+  message Span {
+    string name = 1;
+    uint32 start_index = 2;
+    uint32 end_index = 3;
+  }
+  string str = 1;
+  repeated Span spans = 2;
+}
+
+message UntranslatableSection {
+  uint64 start_index = 1;
+  uint64 end_index = 2;
+}
\ No newline at end of file
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 1006ca9..3457e0b 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -36,6 +36,10 @@
   std::string name;
   uint32_t first_char;
   uint32_t last_char;
+
+  bool operator==(const Span& right) const {
+    return name == right.name && first_char == right.first_char && last_char == right.last_char;
+  }
 };
 
 struct StyleString {
diff --git a/tools/aapt2/ValueTransformer.cpp b/tools/aapt2/ValueTransformer.cpp
index 6eb2e30..2d7996b 100644
--- a/tools/aapt2/ValueTransformer.cpp
+++ b/tools/aapt2/ValueTransformer.cpp
@@ -46,5 +46,6 @@
 VALUE_CREATE_VALUE_DECL(Array);
 VALUE_CREATE_VALUE_DECL(Plural);
 VALUE_CREATE_VALUE_DECL(Styleable);
+VALUE_CREATE_VALUE_DECL(Macro);
 
 }  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/ValueTransformer.h b/tools/aapt2/ValueTransformer.h
index 6925111..6fc4a19 100644
--- a/tools/aapt2/ValueTransformer.h
+++ b/tools/aapt2/ValueTransformer.h
@@ -37,6 +37,7 @@
 struct Array;
 struct Plural;
 struct Styleable;
+struct Macro;
 
 #define AAPT_TRANSFORM_VALUE(T)                                    \
   virtual std::unique_ptr<T> TransformDerived(const T* value) = 0; \
@@ -97,6 +98,7 @@
   AAPT_TRANSFORM_VALUE(Array);
   AAPT_TRANSFORM_VALUE(Plural);
   AAPT_TRANSFORM_VALUE(Styleable);
+  AAPT_TRANSFORM_VALUE(Macro);
 
  protected:
   StringPool* const pool_;
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 4e74ec3..d0c9d89 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -43,6 +43,9 @@
   virtual void Visit(Array* value) { VisitAny(value); }
   virtual void Visit(Plural* value) { VisitAny(value); }
   virtual void Visit(Styleable* value) { VisitAny(value); }
+  virtual void Visit(Macro* value) {
+    VisitAny(value);
+  }
 };
 
 // Const version of ValueVisitor.
@@ -92,6 +95,9 @@
   virtual void Visit(const Styleable* value) {
     VisitAny(value);
   }
+  virtual void Visit(const Macro* value) {
+    VisitAny(value);
+  }
 };
 
 // NOLINT, do not add parentheses around T.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 2c57fb2..e4d0f3b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -462,7 +462,7 @@
   // that existing projects have out-of-date references which pass compilation.
   xml::StripAndroidStudioAttributes(doc->root.get());
 
-  XmlReferenceLinker xml_linker;
+  XmlReferenceLinker xml_linker(table);
   if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
     return {};
   }
@@ -2112,7 +2112,7 @@
         std::unique_ptr<xml::XmlResource> split_manifest =
             GenerateSplitManifest(app_info_, *split_constraints_iter);
 
-        XmlReferenceLinker linker;
+        XmlReferenceLinker linker(&final_table_);
         if (!linker.Consume(context_, split_manifest.get())) {
           context_->GetDiagnostics()->Error(DiagMessage()
                                             << "failed to create Split AndroidManifest.xml");
@@ -2143,7 +2143,7 @@
       // So we give it a package name so it can see local resources.
       manifest_xml->file.name.package = context_->GetCompilationPackage();
 
-      XmlReferenceLinker manifest_linker;
+      XmlReferenceLinker manifest_linker(&final_table_);
       if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
         if (options_.generate_proguard_rules_path &&
             !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index d1e6d39..3118eb8 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -24,6 +24,7 @@
 
 using testing::Eq;
 using testing::HasSubstr;
+using testing::IsNull;
 using testing::Ne;
 using testing::NotNull;
 
@@ -532,4 +533,109 @@
   EXPECT_THAT(*result, Eq(0x01fd0072));
 }
 
+TEST_F(LinkTest, MacroSubstitution) {
+  StdErrDiagnostics diag;
+  const std::string values =
+      R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
+           <macro name="is_enabled">true</macro>
+           <macro name="deep_is_enabled">@macro/is_enabled</macro>
+           <macro name="attr_ref">?is_enabled_attr</macro>
+           <macro name="raw_string">Hello World!</macro>
+           <macro name="android_ref">@an:color/primary_text_dark</macro>
+
+           <attr name="is_enabled_attr" />
+           <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
+
+           <string name="is_enabled_str">@macro/is_enabled</string>
+           <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
+
+           <array name="my_array">
+             <item>@macro/is_enabled</item>
+           </array>
+
+           <style name="MyStyle">
+              <item name="android:background">@macro/attr_ref</item>
+              <item name="android:fontFamily">@macro/raw_string</item>
+           </style>
+         </resources>)";
+
+  const std::string xml_values =
+      R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                     android:background="@macro/android_ref"
+                     android:fontFamily="@macro/raw_string">
+         </SomeLayout>)";
+
+  // Build a library with a public attribute
+  const std::string lib_res = GetTestPath("test-res");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
+  ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
+
+  const std::string lib_apk = GetTestPath("test.apk");
+  // clang-format off
+  auto lib_link_args = LinkCommandBuilder(this)
+      .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+      .AddCompiledResDir(lib_res, &diag)
+      .AddFlag("--no-auto-version")
+      .Build(lib_apk);
+  // clang-format on
+  ASSERT_TRUE(Link(lib_link_args, &diag));
+
+  auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
+  ASSERT_THAT(apk, NotNull());
+
+  // Test that the type flags determines the value type
+  auto actual_bool =
+      test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
+  ASSERT_THAT(actual_bool, NotNull());
+  EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
+  EXPECT_EQ(0xffffffffu, actual_bool->value.data);
+
+  auto actual_str =
+      test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
+  ASSERT_THAT(actual_str, NotNull());
+  EXPECT_EQ(*actual_str->value, "true");
+
+  // Test nested data structures
+  auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
+  ASSERT_THAT(actual_array, NotNull());
+  EXPECT_THAT(actual_array->elements.size(), Eq(1));
+
+  auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
+  ASSERT_THAT(array_el_ref, NotNull());
+  EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
+  EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
+
+  auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
+  ASSERT_THAT(actual_style, NotNull());
+  EXPECT_THAT(actual_style->entries.size(), Eq(2));
+
+  {
+    auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
+    ASSERT_THAT(style_el, NotNull());
+    EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
+    EXPECT_THAT(style_el->id, Eq(0x7f010000));
+  }
+
+  {
+    auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
+    ASSERT_THAT(style_el, NotNull());
+    EXPECT_THAT(*style_el->value, Eq("Hello World!"));
+  }
+
+  // Test substitution in compiled xml files
+  auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
+  ASSERT_THAT(xml, NotNull());
+
+  auto& xml_attrs = xml->root->attributes;
+  ASSERT_THAT(xml_attrs.size(), Eq(2));
+
+  auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
+  ASSERT_THAT(attr_value, NotNull());
+  EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
+  EXPECT_THAT(attr_value->id, Eq(0x01060001));
+
+  EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
+  EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 17d11a6..74ecf47 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -567,13 +567,10 @@
   }
 
   bool FlattenTypes(BigBuffer* buffer) {
-    // Sort the types by their IDs. They will be inserted into the StringPool in
-    // this order.
-
     size_t expected_type_id = 1;
     for (const ResourceTableTypeView& type : package_.types) {
-      if (type.type == ResourceType::kStyleable) {
-        // Styleables aren't real Resource Types, they are represented in the R.java file.
+      if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) {
+        // Styleables and macros are not real resource types.
         continue;
       }
 
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index c24488b..d97e888 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -222,7 +222,7 @@
             android:id="@id/foo"
             app:foo="@id/foo" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   // The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 498d5a2..ec331df 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -656,6 +656,38 @@
     }
     out_ref->name = name_ref.ToResourceName();
   }
+  if (pb_ref.type_flags() != 0) {
+    out_ref->type_flags = pb_ref.type_flags();
+  }
+  out_ref->allow_raw = pb_ref.allow_raw();
+  return true;
+}
+
+static bool DeserializeMacroFromPb(const pb::MacroBody& pb_ref, Macro* out_ref,
+                                   std::string* out_error) {
+  out_ref->raw_value = pb_ref.raw_string();
+
+  if (pb_ref.has_style_string()) {
+    out_ref->style_string.str = pb_ref.style_string().str();
+    for (const auto& span : pb_ref.style_string().spans()) {
+      out_ref->style_string.spans.emplace_back(Span{
+          .name = span.name(), .first_char = span.start_index(), .last_char = span.end_index()});
+    }
+  }
+
+  for (const auto& untranslatable_section : pb_ref.untranslatable_sections()) {
+    out_ref->untranslatable_sections.emplace_back(
+        UntranslatableSection{.start = static_cast<size_t>(untranslatable_section.start_index()),
+                              .end = static_cast<size_t>(untranslatable_section.end_index())});
+  }
+
+  for (const auto& namespace_decls : pb_ref.namespace_stack()) {
+    out_ref->alias_namespaces.emplace_back(
+        Macro::Namespace{.alias = namespace_decls.prefix(),
+                         .package_name = namespace_decls.package_name(),
+                         .is_private = namespace_decls.is_private()});
+  }
+
   return true;
 }
 
@@ -801,6 +833,15 @@
         value = std::move(plural);
       } break;
 
+      case pb::CompoundValue::kMacro: {
+        const pb::MacroBody& pb_macro = pb_compound_value.macro();
+        auto macro = std::make_unique<Macro>();
+        if (!DeserializeMacroFromPb(pb_macro, macro.get(), out_error)) {
+          return {};
+        }
+        value = std::move(macro);
+      } break;
+
       default:
         LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
         break;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index f13f82d..d2f0336 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -440,6 +440,36 @@
   if (ref.is_dynamic) {
     pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
   }
+  if (ref.type_flags) {
+    pb_ref->set_type_flags(*ref.type_flags);
+  }
+  pb_ref->set_allow_raw(ref.allow_raw);
+}
+
+static void SerializeMacroToPb(const Macro& ref, pb::MacroBody* pb_macro) {
+  pb_macro->set_raw_string(ref.raw_value);
+
+  auto pb_style_str = pb_macro->mutable_style_string();
+  pb_style_str->set_str(ref.style_string.str);
+  for (const auto& span : ref.style_string.spans) {
+    auto pb_span = pb_style_str->add_spans();
+    pb_span->set_name(span.name);
+    pb_span->set_start_index(span.first_char);
+    pb_span->set_end_index(span.last_char);
+  }
+
+  for (const auto& untranslatable_section : ref.untranslatable_sections) {
+    auto pb_section = pb_macro->add_untranslatable_sections();
+    pb_section->set_start_index(untranslatable_section.start);
+    pb_section->set_end_index(untranslatable_section.end);
+  }
+
+  for (const auto& namespace_decls : ref.alias_namespaces) {
+    auto pb_namespace = pb_macro->add_namespace_stack();
+    pb_namespace->set_prefix(namespace_decls.alias);
+    pb_namespace->set_package_name(namespace_decls.package_name);
+    pb_namespace->set_is_private(namespace_decls.is_private);
+  }
 }
 
 template <typename T>
@@ -643,6 +673,11 @@
     }
   }
 
+  void Visit(const Macro* macro) override {
+    pb::MacroBody* pb_macro = out_value_->mutable_compound_value()->mutable_macro();
+    SerializeMacroToPb(*macro, pb_macro);
+  }
+
   void VisitAny(const Value* unknown) override {
     LOG(FATAL) << "unimplemented value: " << *unknown;
   }
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 591ba149..e563eda 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -894,4 +894,38 @@
   EXPECT_THAT(*(s->value), Eq("foo"));
 }
 
+TEST(ProtoSerializeTest, SerializeMacro) {
+  auto original = std::make_unique<Macro>();
+  original->raw_value = "\nThis being human is a guest house.";
+  original->style_string.str = " This being human is a guest house.";
+  original->style_string.spans.emplace_back(Span{.name = "b", .first_char = 12, .last_char = 16});
+  original->untranslatable_sections.emplace_back(UntranslatableSection{.start = 12, .end = 17});
+  original->alias_namespaces.emplace_back(
+      Macro::Namespace{.alias = "prefix", .package_name = "package.name", .is_private = true});
+
+  CloningValueTransformer cloner(nullptr);
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+                                             .Add(NewResourceBuilder("com.app.a:macro/foo")
+                                                      .SetValue(original->Transform(cloner))
+                                                      .Build())
+                                             .Build();
+
+  ResourceTable new_table;
+  pb::ResourceTable pb_table;
+  MockFileCollection files;
+  std::string error;
+  SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  EXPECT_THAT(error, IsEmpty());
+
+  Macro* deserialized = test::GetValue<Macro>(&new_table, "com.app.a:macro/foo");
+  ASSERT_THAT(deserialized, NotNull());
+  EXPECT_THAT(deserialized->raw_value, Eq(original->raw_value));
+  EXPECT_THAT(deserialized->style_string.str, Eq(original->style_string.str));
+  EXPECT_THAT(deserialized->style_string.spans, Eq(original->style_string.spans));
+  EXPECT_THAT(deserialized->untranslatable_sections, Eq(original->untranslatable_sections));
+  EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index e1e2e01..de6524d 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -616,8 +616,9 @@
 
   for (const auto& package : table_->packages) {
     for (const auto& type : package->types) {
-      if (type->type == ResourceType::kAttrPrivate) {
-        // We generate these as part of the kAttr type, so skip them here.
+      if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) {
+        // We generate kAttrPrivate as part of the kAttr type, so skip them here.
+        // Macros are not actual resources, so skip them as well.
         continue;
       }
 
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index d08b61e..40395ed 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -570,4 +570,25 @@
   EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
 }
 
+TEST(JavaClassGeneratorTest, SkipMacros) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"android"})
+          .Build();
+  JavaClassGenerator generator(context.get(), table.get(), {});
+
+  std::string output;
+  StringOutputStream out(&output);
+  EXPECT_TRUE(generator.Generate("android", &out));
+  out.Flush();
+
+  EXPECT_THAT(output, Not(HasSubstr("bar")));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b7dfec3..e104066 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -264,7 +264,7 @@
       </View>)");
   foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
 
-  XmlReferenceLinker xml_linker;
+  XmlReferenceLinker xml_linker(nullptr);
   ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
   ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
 
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index c9b8d39..be6c930 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -133,12 +133,14 @@
 // Once an XmlResource is processed by this linker, it is ready to be flattened.
 class XmlReferenceLinker : public IXmlResourceConsumer {
  public:
-  XmlReferenceLinker() = default;
+  explicit XmlReferenceLinker(ResourceTable* table) : table_(table) {
+  }
 
   bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
+  ResourceTable* table_;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8e49fab..4ac25bd 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -21,6 +21,7 @@
 #include "androidfw/ResourceTypes.h"
 
 #include "Diagnostics.h"
+#include "ResourceParser.h"
 #include "ResourceTable.h"
 #include "ResourceUtils.h"
 #include "ResourceValues.h"
@@ -37,128 +38,153 @@
 using ::android::base::StringPrintf;
 
 namespace aapt {
-
 namespace {
-
-// The ReferenceLinkerVisitor will follow all references and make sure they point
-// to resources that actually exist, either in the local resource table, or as external
-// symbols. Once the target resource has been found, the ID of the resource will be assigned
-// to the reference object.
-//
-// NOTE: All of the entries in the ResourceTable must be assigned IDs.
-class ReferenceLinkerVisitor : public DescendingValueVisitor {
- public:
-  using DescendingValueVisitor::Visit;
-
-  ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
-                         StringPool* string_pool, xml::IPackageDeclStack* decl)
-      : callsite_(callsite),
-        context_(context),
-        symbols_(symbols),
-        package_decls_(decl),
-        string_pool_(string_pool) {}
-
-  void Visit(Reference* ref) override {
-    if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
-      error_ = true;
-    }
+struct LoggingResourceName {
+  LoggingResourceName(const Reference& ref, const CallSite& callsite,
+                      const xml::IPackageDeclStack* decls)
+      : ref_(ref), callsite_(callsite), decls_(decls) {
   }
 
-  // We visit the Style specially because during this phase, values of attributes are
-  // all RawString values. Now that we are expected to resolve all symbols, we can
-  // lookup the attributes to find out which types are allowed for the attributes' values.
-  void Visit(Style* style) override {
-    if (style->parent) {
-      Visit(&style->parent.value());
+  const Reference& ref_;
+  const CallSite& callsite_;
+  const xml::IPackageDeclStack* decls_;
+};
+
+inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
+  if (!name.ref_.name) {
+    out << name.ref_.id.value();
+    return out;
+  }
+
+  out << name.ref_.name.value();
+
+  Reference fully_qualified = name.ref_;
+  xml::ResolvePackage(name.decls_, &fully_qualified);
+
+  ResourceName& full_name = fully_qualified.name.value();
+  if (full_name.package.empty()) {
+    full_name.package = name.callsite_.package;
+  }
+
+  if (full_name != name.ref_.name.value()) {
+    out << " (aka " << full_name << ")";
+  }
+  return out;
+}
+
+}  // namespace
+
+std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
+  auto linked_item =
+      ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+  if (linked_item) {
+    auto linked_item_ptr = linked_item.release();
+    if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
+      return std::unique_ptr<Reference>(ref);
     }
+    context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
+                                      << "value of '"
+                                      << LoggingResourceName(*value, callsite_, package_decls_)
+                                      << "' must be a resource reference");
+    delete linked_item_ptr;
+  }
 
-    for (Style::Entry& entry : style->entries) {
-      std::string err_str;
+  error_ = true;
+  return CloningValueTransformer::TransformDerived(value);
+}
 
-      // Transform the attribute reference so that it is using the fully qualified package
-      // name. This will also mark the reference as being able to see private resources if
-      // there was a '*' in the reference or if the package came from the private namespace.
-      Reference transformed_reference = entry.key;
-      ResolvePackage(package_decls_, &transformed_reference);
+std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
+  // We visit the Style specially because during this phase, values of attributes are either
+  // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup
+  // the attributes to find out which types are allowed for the attributes' values.
+  auto new_style = CloningValueTransformer::TransformDerived(style);
+  if (new_style->parent) {
+    new_style->parent = *TransformDerived(&style->parent.value());
+  }
 
-      // Find the attribute in the symbol table and check if it is visible from this callsite.
-      const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
-          transformed_reference, callsite_, context_, symbols_, &err_str);
-      if (symbol) {
-        // Assign our style key the correct ID. The ID may not exist.
-        entry.key.id = symbol->id;
+  for (Style::Entry& entry : new_style->entries) {
+    std::string err_str;
 
-        // Try to convert the value to a more specific, typed value based on the attribute it is
-        // set to.
-        entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
+    // Transform the attribute reference so that it is using the fully qualified package
+    // name. This will also mark the reference as being able to see private resources if
+    // there was a '*' in the reference or if the package came from the private namespace.
+    Reference transformed_reference = entry.key;
+    ResolvePackage(package_decls_, &transformed_reference);
 
-        // Link/resolve the final value (mostly if it's a reference).
-        entry.value->Accept(this);
+    // Find the attribute in the symbol table and check if it is visible from this callsite.
+    const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
+        transformed_reference, callsite_, context_, symbols_, &err_str);
+    if (symbol) {
+      // Assign our style key the correct ID. The ID may not exist.
+      entry.key.id = symbol->id;
 
-        // Now verify that the type of this item is compatible with the
-        // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
-        // check is fast and we avoid creating a DiagMessage when the match is successful.
-        if (!symbol->attribute->Matches(*entry.value, nullptr)) {
-          // The actual type of this item is incompatible with the attribute.
-          DiagMessage msg(entry.key.GetSource());
+      // Link/resolve the final value if it's a reference.
+      entry.value = entry.value->Transform(*this);
 
-          // Call the matches method again, this time with a DiagMessage so we fill in the actual
-          // error message.
-          symbol->attribute->Matches(*entry.value, &msg);
-          context_->GetDiagnostics()->Error(msg);
-          error_ = true;
-        }
+      // Try to convert the value to a more specific, typed value based on the attribute it is
+      // set to.
+      entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
 
-      } else {
+      // Now verify that the type of this item is compatible with the
+      // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
+      // check is fast and we avoid creating a DiagMessage when the match is successful.
+      if (!symbol->attribute->Matches(*entry.value, nullptr)) {
+        // The actual type of this item is incompatible with the attribute.
         DiagMessage msg(entry.key.GetSource());
-        msg << "style attribute '";
-        ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
-        msg << "' " << err_str;
+
+        // Call the matches method again, this time with a DiagMessage so we fill in the actual
+        // error message.
+        symbol->attribute->Matches(*entry.value, &msg);
         context_->GetDiagnostics()->Error(msg);
         error_ = true;
       }
+    } else {
+      context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
+                                        << "style attribute '"
+                                        << LoggingResourceName(entry.key, callsite_, package_decls_)
+                                        << "' " << err_str);
+
+      error_ = true;
     }
   }
+  return new_style;
+}
 
-  bool HasError() {
-    return error_;
+std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
+  auto linked_value =
+      ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+  if (linked_value) {
+    return linked_value;
   }
+  error_ = true;
+  return CloningValueTransformer::TransformDerived(value);
+}
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
+// Transform a RawString value into a more specific, appropriate value, based on the
+// Attribute. If a non RawString value is passed in, this is an identity transform.
+std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
+    std::unique_ptr<Item> value, const Attribute* attr) {
+  if (RawString* raw_string = ValueCast<RawString>(value.get())) {
+    std::unique_ptr<Item> transformed =
+        ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
 
-  // Transform a RawString value into a more specific, appropriate value, based on the
-  // Attribute. If a non RawString value is passed in, this is an identity transform.
-  std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
-                                                const Attribute* attr) {
-    if (RawString* raw_string = ValueCast<RawString>(value.get())) {
-      std::unique_ptr<Item> transformed =
-          ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
-
-      // If we could not parse as any specific type, try a basic STRING.
-      if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
-        StringBuilder string_builder;
-        string_builder.AppendText(*raw_string->value);
-        if (string_builder) {
-          transformed =
-              util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
-        }
-      }
-
-      if (transformed) {
-        return transformed;
+    // If we could not parse as any specific type, try a basic STRING.
+    if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+      StringBuilder string_builder;
+      string_builder.AppendText(*raw_string->value);
+      if (string_builder) {
+        transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
       }
     }
-    return value;
-  }
 
-  const CallSite& callsite_;
-  IAaptContext* context_;
-  SymbolTable* symbols_;
-  xml::IPackageDeclStack* package_decls_;
-  StringPool* string_pool_;
-  bool error_ = false;
-};
+    if (transformed) {
+      return transformed;
+    }
+  }
+  return value;
+}
+
+namespace {
 
 class EmptyDeclStack : public xml::IPackageDeclStack {
  public:
@@ -175,6 +201,27 @@
   DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
 };
 
+struct MacroDeclStack : public xml::IPackageDeclStack {
+  explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
+      : alias_namespaces_(std::move(namespaces)) {
+  }
+
+  Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+    if (alias.empty()) {
+      return xml::ExtractedPackage{{}, true /*private*/};
+    }
+    for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
+      if (alias == StringPiece(it->alias)) {
+        return xml::ExtractedPackage{it->package_name, it->is_private};
+      }
+    }
+    return {};
+  }
+
+ private:
+  std::vector<Macro::Namespace> alias_namespaces_;
+};
+
 // The symbol is visible if it is public, or if the reference to it is requesting private access
 // or if the callsite comes from the same package.
 bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
@@ -220,8 +267,6 @@
       // If the callsite package is the same as the current compilation package,
       // check the feature split dependencies as well. Feature split resources
       // can be referenced without a namespace, just like the base package.
-      // TODO: modify the package name of included splits instead of having the
-      // symbol table look up the resource in in every package. b/136105066
       if (callsite.package == context->GetCompilationPackage()) {
         const auto& split_name_dependencies = context->GetSplitNameDependencies();
         for (const std::string& split_name : split_name_dependencies) {
@@ -295,29 +340,6 @@
   return xml::AaptAttribute(*symbol->attribute, symbol->id);
 }
 
-void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
-                                        const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
-  CHECK(out_msg != nullptr);
-  if (!ref.name) {
-    *out_msg << ref.id.value();
-    return;
-  }
-
-  *out_msg << ref.name.value();
-
-  Reference fully_qualified = ref;
-  xml::ResolvePackage(decls, &fully_qualified);
-
-  ResourceName& full_name = fully_qualified.name.value();
-  if (full_name.package.empty()) {
-    full_name.package = callsite.package;
-  }
-
-  if (full_name != ref.name.value()) {
-    *out_msg << " (aka " << full_name << ")";
-  }
-}
-
 void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
                                          const xml::IPackageDeclStack* decls,
                                          DiagMessage* out_msg) {
@@ -348,18 +370,71 @@
   }
 }
 
-bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
-                                    IAaptContext* context, SymbolTable* symbols,
-                                    const xml::IPackageDeclStack* decls) {
-  CHECK(reference != nullptr);
-  if (!reference->name && !reference->id) {
+std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
+                                                     const Reference& reference,
+                                                     IAaptContext* context, SymbolTable* symbols,
+                                                     ResourceTable* table,
+                                                     const xml::IPackageDeclStack* decls) {
+  if (!reference.name && !reference.id) {
     // This is @null.
-    return true;
+    return std::make_unique<Reference>(reference);
   }
 
-  Reference transformed_reference = *reference;
+  Reference transformed_reference = reference;
   xml::ResolvePackage(decls, &transformed_reference);
 
+  if (transformed_reference.name.value().type == ResourceType::kMacro) {
+    if (transformed_reference.name.value().package.empty()) {
+      transformed_reference.name.value().package = callsite.package;
+    }
+
+    auto result = table->FindResource(transformed_reference.name.value());
+    if (!result || result.value().entry->values.empty()) {
+      context->GetDiagnostics()->Error(
+          DiagMessage(reference.GetSource())
+          << "failed to find definition for "
+          << LoggingResourceName(transformed_reference, callsite, decls));
+      return {};
+    }
+
+    auto& macro_values = result.value().entry->values;
+    CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
+
+    auto macro = ValueCast<Macro>(macro_values[0]->value.get());
+    CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
+                            << *macro_values[0]->value << ")";
+
+    // Re-create the state used to parse the macro tag to compile the macro contents as if it was
+    // defined inline
+    uint32_t type_flags = 0;
+    if (reference.type_flags.has_value()) {
+      type_flags = reference.type_flags.value();
+    }
+
+    MacroDeclStack namespace_stack(macro->alias_namespaces);
+    FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
+                                 .style_string = macro->style_string,
+                                 .untranslatable_sections = macro->untranslatable_sections,
+                                 .namespace_resolver = &namespace_stack,
+                                 .source = macro->GetSource()};
+
+    auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
+                                              macro_values[0]->config, *context->GetDiagnostics());
+    if (new_value == nullptr) {
+      context->GetDiagnostics()->Error(
+          DiagMessage(reference.GetSource())
+          << "failed to substitute macro "
+          << LoggingResourceName(transformed_reference, callsite, decls)
+          << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
+      return {};
+    }
+
+    if (auto ref = ValueCast<Reference>(new_value.get())) {
+      return LinkReference(callsite, *ref, context, symbols, table, decls);
+    }
+    return new_value;
+  }
+
   std::string err_str;
   const SymbolTable::Symbol* s =
       ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
@@ -367,17 +442,17 @@
     // The ID may not exist. This is fine because of the possibility of building
     // against libraries without assigned IDs.
     // Ex: Linking against own resources when building a static library.
-    reference->id = s->id;
-    reference->is_dynamic = s->is_dynamic;
-    return true;
+    auto new_ref = std::make_unique<Reference>(reference);
+    new_ref->id = s->id;
+    new_ref->is_dynamic = s->is_dynamic;
+    return std::move(new_ref);
   }
 
-  DiagMessage error_msg(reference->GetSource());
-  error_msg << "resource ";
-  WriteResourceName(*reference, callsite, decls, &error_msg);
-  error_msg << " " << err_str;
-  context->GetDiagnostics()->Error(error_msg);
-  return false;
+  context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
+                                   << "resource "
+                                   << LoggingResourceName(transformed_reference, callsite, decls)
+                                   << " " << err_str);
+  return {};
 }
 
 bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
@@ -412,14 +487,15 @@
 
         // The context of this resource is the package in which it is defined.
         const CallSite callsite{name.package};
-        ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
-                                       &table->string_pool, &decl_stack);
+        ReferenceLinkerTransformer reference_transformer(callsite, context,
+                                                         context->GetExternalSymbols(),
+                                                         &table->string_pool, table, &decl_stack);
 
         for (auto& config_value : entry->values) {
-          config_value->value->Accept(&visitor);
+          config_value->value = config_value->value->Transform(reference_transformer);
         }
 
-        if (visitor.HasError()) {
+        if (reference_transformer.HasError()) {
           error = true;
         }
       }
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 1256709..770f1e5 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -28,6 +28,41 @@
 
 namespace aapt {
 
+// A ValueTransformer that returns fully linked versions of resource and macro references.
+class ReferenceLinkerTransformer : public CloningValueTransformer {
+ public:
+  ReferenceLinkerTransformer(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+                             StringPool* string_pool, ResourceTable* table,
+                             xml::IPackageDeclStack* decl)
+      : CloningValueTransformer(string_pool),
+        callsite_(callsite),
+        context_(context),
+        symbols_(symbols),
+        table_(table),
+        package_decls_(decl) {
+  }
+
+  std::unique_ptr<Reference> TransformDerived(const Reference* value) override;
+  std::unique_ptr<Item> TransformItem(const Reference* value) override;
+  std::unique_ptr<Style> TransformDerived(const Style* value) override;
+
+  bool HasError() {
+    return error_;
+  }
+
+ private:
+  // Transform a RawString value into a more specific, appropriate value, based on the
+  // Attribute. If a non RawString value is passed in, this is an identity transform.
+  std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value, const Attribute* attr);
+
+  const CallSite& callsite_;
+  IAaptContext* context_;
+  SymbolTable* symbols_;
+  ResourceTable* table_;
+  xml::IPackageDeclStack* package_decls_;
+  bool error_ = false;
+};
+
 // Resolves all references to resources in the ResourceTable and assigns them IDs.
 // The ResourceTable must already have IDs assigned to each resource.
 // Once the ResourceTable is processed by this linker, it is ready to be flattened.
@@ -70,19 +105,28 @@
 
   // Writes the resource name to the DiagMessage, using the
   // "orig_name (aka <transformed_name>)" syntax.
-  static void WriteResourceName(const Reference& orig, const CallSite& callsite,
-                                const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+  /*static void WriteResourceName(const Reference& orig, const CallSite& callsite,
+                                const xml::IPackageDeclStack* decls, DiagMessage* out_msg);*/
 
   // Same as WriteResourceName but omits the 'attr' part.
   static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
                                  const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
 
-  // Transforms the package name of the reference to the fully qualified package name using
-  // the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
-  // to the reference at the callsite, the reference is updated with an ID.
-  // Returns false on failure, and an error message is logged to the IDiagnostics in the context.
-  static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
-                            SymbolTable* symbols, const xml::IPackageDeclStack* decls);
+  // Returns a fully linked version a resource reference.
+  //
+  // If the reference points to a non-macro resource, the xml::IPackageDeclStack is used to
+  // determine the fully qualified name of the referenced resource. If the symbol is visible
+  // to the reference at the callsite, a copy of the reference with an updated updated ID is
+  // returned.
+  //
+  // If the reference points to a macro, the ResourceTable is used to find the macro definition and
+  // substitute its contents in place of the reference.
+  //
+  // Returns nullptr on failure, and an error message is logged to the IDiagnostics in the context.
+  static std::unique_ptr<Item> LinkReference(const CallSite& callsite, const Reference& reference,
+                                             IAaptContext* context, SymbolTable* symbols,
+                                             ResourceTable* table,
+                                             const xml::IPackageDeclStack* decls);
 
   // Links all references in the ResourceTable.
   bool Consume(IAaptContext* context, ResourceTable* table) override;
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 228c5bd74..2d8f0d3 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -365,4 +365,22 @@
   EXPECT_THAT(s, IsNull());
 }
 
+TEST(ReferenceLinkerTest, MacroFailToFindDefinition) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddReference("com.app.test:string/foo", ResourceId(0x7f020000), "com.app.test:macro/bar")
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .SetCompilationPackage("com.app.test")
+          .SetPackageId(0x7f)
+          .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .Build();
+
+  ReferenceLinker linker;
+  ASSERT_FALSE(linker.Consume(context.get(), table.get()));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index a98ab0f..d638096 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -82,7 +82,7 @@
           app:foo="16dp"
           foo="bar"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   XmlCompatVersioner::Rules rules;
@@ -121,7 +121,7 @@
           app:foo="16dp"
           foo="bar"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   XmlCompatVersioner::Rules rules;
@@ -181,7 +181,7 @@
       <View xmlns:android="http://schemas.android.com/apk/res/android"
           android:paddingHorizontal="24dp" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   XmlCompatVersioner::Rules rules;
@@ -256,7 +256,7 @@
           android:paddingLeft="16dp"
           android:paddingRight="16dp"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   Item* padding_horizontal_value =
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index c3c16b9..aaa085e 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -33,49 +33,18 @@
 
 namespace {
 
-// Visits all references (including parents of styles, references in styles, arrays, etc) and
-// links their symbolic name to their Resource ID, performing mangling and package aliasing
-// as needed.
-class ReferenceVisitor : public DescendingValueVisitor {
- public:
-  using DescendingValueVisitor::Visit;
-
-  ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
-                   xml::IPackageDeclStack* decls)
-      : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {}
-
-  void Visit(Reference* ref) override {
-    if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) {
-      error_ = true;
-    }
-  }
-
-  bool HasError() const {
-    return error_;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
-
-  const CallSite& callsite_;
-  IAaptContext* context_;
-  SymbolTable* symbols_;
-  xml::IPackageDeclStack* decls_;
-  bool error_;
-};
-
 // Visits each xml Element and compiles the attributes within.
 class XmlVisitor : public xml::PackageAwareVisitor {
  public:
   using xml::PackageAwareVisitor::Visit;
 
-  XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
-             SymbolTable* symbols)
+  XmlVisitor(const Source& source, StringPool* pool, const CallSite& callsite,
+             IAaptContext* context, ResourceTable* table, SymbolTable* symbols)
       : source_(source),
         callsite_(callsite),
         context_(context),
         symbols_(symbols),
-        reference_visitor_(callsite, context, symbols, this) {
+        reference_transformer_(callsite, context, symbols, pool, table, this) {
   }
 
   void Visit(xml::Element* el) override {
@@ -127,7 +96,7 @@
       if (attr.compiled_value) {
         // With a compiledValue, we must resolve the reference and assign it an ID.
         attr.compiled_value->SetSource(source);
-        attr.compiled_value->Accept(&reference_visitor_);
+        attr.compiled_value = attr.compiled_value->Transform(reference_transformer_);
       } else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
         // We won't be able to encode this as a string.
         DiagMessage msg(source);
@@ -143,7 +112,7 @@
   }
 
   bool HasError() {
-    return error_ || reference_visitor_.HasError();
+    return error_ || reference_transformer_.HasError();
   }
 
  private:
@@ -154,7 +123,7 @@
   IAaptContext* context_;
   SymbolTable* symbols_;
 
-  ReferenceVisitor reference_visitor_;
+  ReferenceLinkerTransformer reference_transformer_;
   bool error_ = false;
 };
 
@@ -173,7 +142,8 @@
     callsite.package = context->GetCompilationPackage();
   }
 
-  XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
+  XmlVisitor visitor(resource->file.source, &resource->string_pool, callsite, context, table_,
+                     context->GetExternalSymbols());
   if (resource->root) {
     resource->root->Accept(&visitor);
     return !visitor.HasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 0ce2e50..ddf5b9a 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -91,7 +91,7 @@
             nonAaptAttrRef="@id/id"
             class="hello" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -144,7 +144,7 @@
       <View xmlns:android="http://schemas.android.com/apk/res/android"
           android:colorAccent="@android:color/hidden" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
 }
 
@@ -153,7 +153,7 @@
     <View xmlns:android="http://schemas.android.com/apk/res/android"
           android:colorAccent="@*android:color/hidden" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 }
 
@@ -162,7 +162,7 @@
       <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
           support:colorAccent="#ff0000" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -181,7 +181,7 @@
       <View xmlns:app="http://schemas.android.com/apk/res-auto"
           app:colorAccent="@app:color/red" />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -203,7 +203,7 @@
         <View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
       </View>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -239,7 +239,7 @@
       <View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
           android:attr="@id/id"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* view_el = doc->root.get();
@@ -261,7 +261,7 @@
   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
     <gradient />)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* gradient_el = doc->root.get();
@@ -283,7 +283,7 @@
   <gradient xmlns:android="http://schemas.android.com/apk/res/android"
       android:angle="90"/>)");
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* gradient_el = doc->root.get();
@@ -305,7 +305,7 @@
   <gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
   context_->SetMinSdkVersion(30);
 
-  XmlReferenceLinker linker;
+  XmlReferenceLinker linker(nullptr);
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 
   xml::Element* gradient_el = doc->root.get();
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index a023494..182203d 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -177,6 +177,10 @@
   return event_queue_.front().data2;
 }
 
+const std::vector<XmlPullParser::PackageDecl>& XmlPullParser::package_decls() const {
+  return package_aliases_;
+}
+
 XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
   return event_queue_.front().attributes.begin();
 }
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 6ebaa28..5da2d4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -123,6 +123,13 @@
    */
   Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
 
+  struct PackageDecl {
+    std::string prefix;
+    ExtractedPackage package;
+  };
+
+  const std::vector<PackageDecl>& package_decls() const;
+
   //
   // Remaining methods are for retrieving information about attributes
   // associated with a StartElement.
@@ -180,11 +187,6 @@
   const std::string empty_;
   size_t depth_;
   std::stack<std::string> namespace_uris_;
-
-  struct PackageDecl {
-    std::string prefix;
-    ExtractedPackage package;
-  };
   std::vector<PackageDecl> package_aliases_;
 };