Merge "Import translations. DO NOT MERGE"
diff --git a/Android.bp b/Android.bp
index 69d654f..2dc8a4c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -263,8 +263,6 @@
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         ":libupdate_engine_aidl",
-        // TODO: this needs to be removed when statsd-framework.jar is separated out
-        ":statsd_java_aidl",
         ":storaged_aidl",
         ":vold_aidl",
 
@@ -445,13 +443,6 @@
 }
 
 filegroup {
-    name: "graphicsstats_proto",
-    srcs: [
-        "libs/hwui/protos/graphicsstats.proto",
-    ],
-}
-
-filegroup {
     name: "libvibrator_aidl",
     srcs: [
         "core/java/android/os/IExternalVibrationController.aidl",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 661f32f..c458d11 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -670,7 +670,7 @@
     private void startApp(int userId, String packageName) throws RemoteException {
         final Context context = InstrumentationRegistry.getContext();
         final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
-                context.getPackageName(),
+                context.getPackageName(), context.getFeatureId(),
                 context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
                 null, 0, 0, null, null, userId);
         attestTrue("User " + userId + " failed to start " + packageName,
diff --git a/apex/permission/testing/Android.bp b/apex/permission/testing/Android.bp
index f8978dc..63bf0a0 100644
--- a/apex/permission/testing/Android.bp
+++ b/apex/permission/testing/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-apex {
+apex_test {
     name: "test_com.android.permission",
     visibility: [
         "//system/apex/tests",
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
index 6fb7ef4..900193a 100644
--- a/apex/sdkextensions/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
@@ -69,7 +69,7 @@
     auto itr = std::min_element(versions.begin(), versions.end());
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
-    if (!android::base::SetProperty("ro.build.version.extensions.r", prop_value)) {
+    if (!android::base::SetProperty("build.version.extensions.r", prop_value)) {
         LOG(ERROR) << "failed to set sdk_info prop";
         return EXIT_FAILURE;
     }
diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
index a8a7eff..103b53e 100644
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
@@ -38,7 +38,7 @@
 
     private static final int R_EXTENSION_INT;
     static {
-        R_EXTENSION_INT = SystemProperties.getInt("ro.build.version.extensions.r", 0);
+        R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
     }
 
     /**
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 6d639fd..db5f439 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -13,35 +13,31 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-
-// TODO(b/145815909): move StatsDimensionsValue.aidl here
 filegroup {
-    name: "statsd_aidl",
+    name: "statsd_java_aidl",
+    srcs: ["**/*.aidl"],
+}
+
+aidl_interface {
+    name: "statsd-aidl",
     srcs: [
         "android/os/IPendingIntentRef.aidl",
         "android/os/IPullAtomCallback.aidl",
         "android/os/IPullAtomResultReceiver.aidl",
         "android/os/IStatsCompanionService.aidl",
         "android/os/IStatsd.aidl",
+        "android/os/StatsDimensionsValueParcel.aidl",
         "android/util/StatsEventParcel.aidl",
     ],
-}
-
-filegroup {
-    name: "statsd_java_aidl",
-    srcs: ["**/*.aidl"],
-}
-
-// This library is currently unused
-aidl_interface {
-    name: "stats-event-parcel-aidl",
-    srcs: ["android/util/StatsEventParcel.aidl"],
     backend: {
         java: {
-            sdk_version: "28",
+            enabled: false, // the platform uses statsd_java_aidl
         },
         cpp: {
-            enabled: false,
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
         }
     }
 }
diff --git a/apex/statsd/aidl/android/os/IPendingIntentRef.aidl b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
index 6b9e467..000a699 100644
--- a/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
+++ b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
@@ -16,7 +16,7 @@
 
 package android.os;
 
-import android.os.StatsDimensionsValue;
+import android.os.StatsDimensionsValueParcel;
 
 /**
   * Binder interface to hold a PendingIntent for StatsCompanionService.
@@ -42,5 +42,5 @@
       */
      oneway void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
                                          long subscriptionRuleId, in String[] cookies,
-                                         in StatsDimensionsValue dimensionsValue);
-}
\ No newline at end of file
+                                         in StatsDimensionsValueParcel dimensionsValueParcel);
+}
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index 253b2c1..10b1e5b 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -222,12 +222,6 @@
     const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
 
     /**
-     * Logs an event for watchdog rollbacks.
-     */
-     oneway void sendWatchdogRollbackOccurredAtom(in int rollbackType, in String packageName,
-         in long packageVersionCode, in int rollbackReason, in String failingPackageName);
-
-    /**
      * Returns the most recently registered experiment IDs.
      */
     long[] getRegisteredExperimentIds();
diff --git a/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl b/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl
new file mode 100644
index 0000000..a8685e3
--- /dev/null
+++ b/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl
@@ -0,0 +1,21 @@
+package android.os;
+
+/**
+ * @hide
+ */
+parcelable StatsDimensionsValueParcel {
+    /**
+     * Field equals:
+     *      - atomTag for top level StatsDimensionsValueParcel
+     *      - position in dimension for all other levels
+     */
+    int field;
+    int valueType;
+
+    String stringValue;
+    int intValue;
+    long longValue;
+    boolean boolValue;
+    float floatValue;
+    StatsDimensionsValueParcel[] tupleValue;
+}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index d85ae69..e3c70d7 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -15,9 +15,9 @@
 filegroup {
     name: "framework-statsd-sources",
     srcs: [
-    "java/**/*.java",
+        "java/**/*.java",
+        ":statsd_java_aidl",
     ],
-    path: "java",
 }
 
 java_library {
diff --git a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
index 886130f..71d4359 100644
--- a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
+++ b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
@@ -96,6 +96,47 @@
     }
 
     /**
+     * Creates a {@code StatsDimensionsValue} from a StatsDimensionsValueParcel
+     * TODO(b/149103391): Make StatsDimensionsValue a wrapper on top of
+     * StatsDimensionsValueParcel.
+     *
+     * @hide
+     */
+    public StatsDimensionsValue(StatsDimensionsValueParcel parcel) {
+        mField = parcel.field;
+        mValueType = parcel.valueType;
+        switch (mValueType) {
+            case STRING_VALUE_TYPE:
+                mValue = parcel.stringValue;
+                break;
+            case INT_VALUE_TYPE:
+                mValue = parcel.intValue;
+                break;
+            case LONG_VALUE_TYPE:
+                mValue = parcel.longValue;
+                break;
+            case BOOLEAN_VALUE_TYPE:
+                mValue = parcel.boolValue;
+                break;
+            case FLOAT_VALUE_TYPE:
+                mValue = parcel.floatValue;
+                break;
+            case TUPLE_VALUE_TYPE:
+                StatsDimensionsValue[] values = new StatsDimensionsValue[parcel.tupleValue.length];
+                for (int i = 0; i < parcel.tupleValue.length; i++) {
+                    values[i] = new StatsDimensionsValue(parcel.tupleValue[i]);
+                }
+                mValue = values;
+                break;
+            default:
+                Slog.w(TAG, "StatsDimensionsValueParcel contains bad valueType: " + mValueType);
+                mValue = null;
+                break;
+        }
+    }
+
+
+    /**
      * Return the field, i.e. the tag of a statsd atom.
      *
      * @return the field
diff --git a/apex/statsd/framework/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java
index 7910737..e7659d8 100644
--- a/apex/statsd/framework/java/android/util/StatsLog.java
+++ b/apex/statsd/framework/java/android/util/StatsLog.java
@@ -171,52 +171,11 @@
                 state,
                 proto.getBytes(),
                 0,
-                0);
+                0,
+                false);
         return true;
     }
 
-    /**
-     * Logs an event for watchdog rollbacks.
-     *
-     * @param rollbackType          state of the rollback.
-     * @param packageName           package name being rolled back.
-     * @param packageVersionCode    version of the package being rolled back.
-     * @param rollbackReason        reason the package is being rolled back.
-     * @param failingPackageName    the package name causing the failure.
-     *
-     * @return True if the log request was sent to statsd.
-     *
-     * @hide
-     */
-    @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
-    public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName,
-            long packageVersionCode, int rollbackReason, String failingPackageName) {
-        synchronized (sLogLock) {
-            try {
-                IStatsd service = getIStatsdLocked();
-                if (service == null) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Failed to find statsd when logging event");
-                    }
-                    return false;
-                }
-
-                service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName,
-                        packageVersionCode, rollbackReason, failingPackageName);
-                return true;
-            } catch (RemoteException e) {
-                sService = null;
-                if (DEBUG) {
-                    Slog.d(TAG,
-                            "Failed to connect to StatsCompanionService when logging "
-                                    + "WatchdogRollbackOccurred");
-                }
-                return false;
-            }
-        }
-    }
-
-
     private static IStatsd getIStatsdLocked() throws RemoteException {
         if (sService != null) {
             return sService;
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
index 4495dc9..c1ba73f 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
@@ -24,7 +24,8 @@
 import android.os.IPendingIntentRef;
 import android.os.Process;
 import android.os.StatsDimensionsValue;
-import android.util.Slog;
+import android.os.StatsDimensionsValueParcel;
+import android.util.Log;
 
 import com.android.server.SystemService;
 
@@ -40,6 +41,9 @@
 
     private static final int AID_STATSD = 1066;
 
+    private static final String STATS_COMPANION_SERVICE = "statscompanion";
+    private static final String STATS_MANAGER_SERVICE = "statsmanager";
+
     static void enforceStatsdCallingUid() {
         if (Binder.getCallingPid() == Process.myPid()) {
             return;
@@ -68,14 +72,12 @@
             mStatsManagerService.setStatsCompanionService(mStatsCompanionService);
 
             try {
-                publishBinderService(Context.STATS_COMPANION_SERVICE,
-                        mStatsCompanionService);
-                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
-                publishBinderService(Context.STATS_MANAGER_SERVICE,
-                        mStatsManagerService);
-                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_MANAGER_SERVICE);
+                publishBinderService(STATS_COMPANION_SERVICE, mStatsCompanionService);
+                if (DEBUG) Log.d(TAG, "Published " + STATS_COMPANION_SERVICE);
+                publishBinderService(STATS_MANAGER_SERVICE, mStatsManagerService);
+                if (DEBUG) Log.d(TAG, "Published " + STATS_MANAGER_SERVICE);
             } catch (Exception e) {
-                Slog.e(TAG, "Failed to publishBinderService", e);
+                Log.e(TAG, "Failed to publishBinderService", e);
             }
         }
 
@@ -124,7 +126,7 @@
             try {
                 mPendingIntent.send(mContext, CODE_DATA_BROADCAST, intent, null, null);
             } catch (PendingIntent.CanceledException e) {
-                Slog.w(TAG, "Unable to send PendingIntent");
+                Log.w(TAG, "Unable to send PendingIntent");
             }
         }
 
@@ -136,17 +138,19 @@
             try {
                 mPendingIntent.send(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
                 if (DEBUG) {
-                    Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
+                    Log.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
                 }
             } catch (PendingIntent.CanceledException e) {
-                Slog.w(TAG, "Unable to send active configs changed broadcast using PendingIntent");
+                Log.w(TAG, "Unable to send active configs changed broadcast using PendingIntent");
             }
         }
 
         @Override
         public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
-                long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) {
+                long subscriptionRuleId, String[] cookies,
+                StatsDimensionsValueParcel dimensionsValueParcel) {
             enforceStatsdCallingUid();
+            StatsDimensionsValue dimensionsValue = new StatsDimensionsValue(dimensionsValueParcel);
             Intent intent =
                     new Intent()
                             .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
@@ -162,7 +166,7 @@
                     StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
 
             if (DEBUG) {
-                Slog.d(TAG,
+                Log.d(TAG,
                         String.format(
                                 "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
                                 configUid, configId, subscriptionId, subscriptionRuleId,
@@ -172,7 +176,7 @@
             try {
                 mPendingIntent.send(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
             } catch (PendingIntent.CanceledException e) {
-                Slog.w(TAG,
+                Log.w(TAG,
                         "Unable to send using PendingIntent from uid " + configUid
                                 + "; presumably it had been cancelled.");
             }
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index a735cb8..cb167c3 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -40,14 +40,10 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.Slog;
+import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.LooperStats;
-import com.android.internal.util.DumpUtils;
-import com.android.server.BinderCallsStatsService;
-import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
 
@@ -89,6 +85,12 @@
 
     public static final int DEATH_THRESHOLD = 10;
 
+    // TODO(b/149090705): Implement an alternative to sending broadcast with @hide flag
+    // FLAG_RECEIVER_INCLUDE_BACKGROUND. Instead of using the flag, find the
+    // list of registered broadcast receivers and send them directed broadcasts
+    // to wake them up. See b/147374337.
+    private static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
+
     static final class CompanionHandler extends Handler {
         CompanionHandler(Looper looper) {
             super(looper);
@@ -126,7 +128,7 @@
             public void onReceive(Context context, Intent intent) {
                 synchronized (sStatsdLock) {
                     if (sStatsd == null) {
-                        Slog.w(TAG, "Could not access statsd for UserUpdateReceiver");
+                        Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
                         return;
                     }
                     try {
@@ -134,14 +136,14 @@
                         // Needed since the new user basically has a version of every app.
                         informAllUidsLocked(context);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
+                        Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
                         forgetEverythingLocked();
                     }
                 }
             }
         };
         mShutdownEventReceiver = new ShutdownEventReceiver();
-        if (DEBUG) Slog.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
+        if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
         HandlerThread handlerThread = new HandlerThread(TAG);
         handlerThread.start();
         mHandler = new CompanionHandler(handlerThread.getLooper());
@@ -171,21 +173,21 @@
         PackageManager pm = context.getPackageManager();
         final List<UserHandle> users = um.getUserHandles(true);
         if (DEBUG) {
-            Slog.d(TAG, "Iterating over " + users.size() + " userHandles.");
+            Log.d(TAG, "Iterating over " + users.size() + " userHandles.");
         }
 
         ParcelFileDescriptor[] fds;
         try {
             fds = ParcelFileDescriptor.createPipe();
         } catch (IOException e) {
-            Slog.e(TAG, "Failed to create a pipe to send uid map data.", e);
+            Log.e(TAG, "Failed to create a pipe to send uid map data.", e);
             return;
         }
         sStatsd.informAllUidData(fds[0]);
         try {
             fds[0].close();
         } catch (IOException e) {
-            Slog.e(TAG, "Failed to close the read side of the pipe.", e);
+            Log.e(TAG, "Failed to close the read side of the pipe.", e);
         }
         final ParcelFileDescriptor writeFd = fds[1];
         HandlerThread backgroundThread = new HandlerThread(
@@ -239,7 +241,7 @@
                 }
                 output.flush();
                 if (DEBUG) {
-                    Slog.d(TAG, "Sent data for " + numRecords + " apps");
+                    Log.d(TAG, "Sent data for " + numRecords + " apps");
                 }
             } finally {
                 IoUtils.closeQuietly(fout);
@@ -261,10 +263,10 @@
                     && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                 return; // Keep only replacing or normal add and remove.
             }
-            if (DEBUG) Slog.d(TAG, "StatsCompanionService noticed an app was updated.");
+            if (DEBUG) Log.d(TAG, "StatsCompanionService noticed an app was updated.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of an app update");
+                    Log.w(TAG, "Could not access statsd to inform it of an app update");
                     return;
                 }
                 try {
@@ -299,7 +301,7 @@
                                 installer == null ? "" : installer);
                     }
                 } catch (Exception e) {
-                    Slog.w(TAG, "Failed to inform statsd of an app update", e);
+                    Log.w(TAG, "Failed to inform statsd of an app update", e);
                 }
             }
         }
@@ -308,18 +310,18 @@
     public final static class AnomalyAlarmListener implements OnAlarmListener {
         @Override
         public void onAlarm() {
-            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+            Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
                     + System.currentTimeMillis() + "ms.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+                    Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
                     return;
                 }
                 try {
                     // Two-way call to statsd to retain AlarmManager wakelock
                     sStatsd.informAnomalyAlarmFired();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+                    Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
                 }
             }
             // AlarmManager releases its own wakelock here.
@@ -330,18 +332,18 @@
         @Override
         public void onAlarm() {
             if (DEBUG) {
-                Slog.d(TAG, "Time to poll something.");
+                Log.d(TAG, "Time to poll something.");
             }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
+                    Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
                     return;
                 }
                 try {
                     // Two-way call to statsd to retain AlarmManager wakelock
                     sStatsd.informPollAlarmFired();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
+                    Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
                 }
             }
         }
@@ -351,18 +353,18 @@
         @Override
         public void onAlarm() {
             if (DEBUG) {
-                Slog.d(TAG, "Time to trigger periodic alarm.");
+                Log.d(TAG, "Time to trigger periodic alarm.");
             }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
+                    Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
                     return;
                 }
                 try {
                     // Two-way call to statsd to retain AlarmManager wakelock
                     sStatsd.informAlarmForSubscriberTriggeringFired();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
+                    Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
                 }
             }
             // AlarmManager releases its own wakelock here.
@@ -381,16 +383,16 @@
                 return;
             }
 
-            Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
+            Log.i(TAG, "StatsCompanionService noticed a shutdown.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of a shutdown event.");
+                    Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
                     return;
                 }
                 try {
                     sStatsd.informDeviceShutdown();
                 } catch (Exception e) {
-                    Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
+                    Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
                 }
             }
         }
@@ -399,7 +401,7 @@
     @Override // Binder call
     public void setAnomalyAlarm(long timestampMs) {
         StatsCompanion.enforceStatsdCallingUid();
-        if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
+        if (DEBUG) Log.d(TAG, "Setting anomaly alarm for " + timestampMs);
         final long callingToken = Binder.clearCallingIdentity();
         try {
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
@@ -415,7 +417,7 @@
     @Override // Binder call
     public void cancelAnomalyAlarm() {
         StatsCompanion.enforceStatsdCallingUid();
-        if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
+        if (DEBUG) Log.d(TAG, "Cancelling anomaly alarm");
         final long callingToken = Binder.clearCallingIdentity();
         try {
             mAlarmManager.cancel(mAnomalyAlarmListener);
@@ -428,7 +430,7 @@
     public void setAlarmForSubscriberTriggering(long timestampMs) {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG,
+            Log.d(TAG,
                     "Setting periodic alarm in about " + (timestampMs
                             - SystemClock.elapsedRealtime()));
         }
@@ -447,7 +449,7 @@
     public void cancelAlarmForSubscriberTriggering() {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "Cancelling periodic alarm");
+            Log.d(TAG, "Cancelling periodic alarm");
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
@@ -461,7 +463,7 @@
     public void setPullingAlarm(long nextPullTimeMs) {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "Setting pulling alarm in about "
+            Log.d(TAG, "Setting pulling alarm in about "
                     + (nextPullTimeMs - SystemClock.elapsedRealtime()));
         }
         final long callingToken = Binder.clearCallingIdentity();
@@ -479,7 +481,7 @@
     public void cancelPullingAlarm() {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "Cancelling pulling alarm");
+            Log.d(TAG, "Cancelling pulling alarm");
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
@@ -493,11 +495,11 @@
     public void statsdReady() {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "learned that statsdReady");
+            Log.d(TAG, "learned that statsdReady");
         }
         sayHiToStatsd(); // tell statsd that we're ready too and link to it
         mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
-                        .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
+                        .addFlags(FLAG_RECEIVER_INCLUDE_BACKGROUND),
                 UserHandle.SYSTEM, android.Manifest.permission.DUMP);
     }
 
@@ -509,7 +511,7 @@
             try {
                 informAllUidsLocked(mContext);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+                Log.e(TAG, "Failed to trigger uid snapshot.", e);
             } finally {
                 restoreCallingIdentity(token);
             }
@@ -540,7 +542,7 @@
      * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
      */
     void systemReady() {
-        if (DEBUG) Slog.d(TAG, "Learned that systemReady");
+        if (DEBUG) Log.d(TAG, "Learned that systemReady");
         sayHiToStatsd();
     }
 
@@ -555,27 +557,27 @@
     private void sayHiToStatsd() {
         synchronized (sStatsdLock) {
             if (sStatsd != null) {
-                Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
+                Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
                         new IllegalStateException(
                                 "sStatsd is not null when being fetched"));
                 return;
             }
             sStatsd = fetchStatsdService();
             if (sStatsd == null) {
-                Slog.i(TAG,
+                Log.i(TAG,
                         "Could not yet find statsd to tell it that StatsCompanion is "
                                 + "alive.");
                 return;
             }
             mStatsManagerService.statsdReady(sStatsd);
-            if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
+            if (DEBUG) Log.d(TAG, "Saying hi to statsd");
             try {
                 sStatsd.statsCompanionReady();
                 // If the statsCompanionReady two-way binder call returns, link to statsd.
                 try {
                     sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
                 } catch (RemoteException e) {
-                    Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+                    Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
                     forgetEverythingLocked();
                 }
                 // Setup broadcast receiver for updates.
@@ -605,9 +607,9 @@
                 } finally {
                     restoreCallingIdentity(token);
                 }
-                Slog.i(TAG, "Told statsd that StatsCompanionService is alive.");
+                Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+                Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
                 forgetEverythingLocked();
             }
         }
@@ -616,7 +618,7 @@
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
-            Slog.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
+            Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
             synchronized (sStatsdLock) {
                 long now = SystemClock.elapsedRealtime();
                 for (Long timeMillis : mDeathTimeMillis) {
@@ -656,22 +658,15 @@
         cancelAnomalyAlarm();
         cancelPullingAlarm();
 
-        BinderCallsStatsService.Internal binderStats =
-                LocalServices.getService(BinderCallsStatsService.Internal.class);
-        if (binderStats != null) {
-            binderStats.reset();
-        }
-
-        LooperStats looperStats = LocalServices.getService(LooperStats.class);
-        if (looperStats != null) {
-            looperStats.reset();
-        }
         mStatsManagerService.statsdNotReady();
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
 
         synchronized (sStatsdLock) {
             writer.println(
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index c1dc584..4e4bc40 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -30,7 +30,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
-import android.util.Slog;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -192,7 +192,7 @@
             statsd.registerPullAtomCallback(
                     callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
+            Log.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -219,7 +219,7 @@
         try {
             statsd.unregisterPullAtomCallback(callingUid, atomTag);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
+            Log.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -243,7 +243,7 @@
                 statsd.setDataFetchOperation(configId, pir, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to setDataFetchOperation with statsd");
+            Log.e(TAG, "Failed to setDataFetchOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -264,7 +264,7 @@
                 statsd.removeDataFetchOperation(configId, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to removeDataFetchOperation with statsd");
+            Log.e(TAG, "Failed to removeDataFetchOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -287,7 +287,7 @@
                 return statsd.setActiveConfigsChangedOperation(pir, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
+            Log.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -308,7 +308,7 @@
                 statsd.removeActiveConfigsChangedOperation(callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
+            Log.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -336,7 +336,7 @@
                         configId, subscriberId, pir, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to setBroadcastSubscriber with statsd");
+            Log.e(TAG, "Failed to setBroadcastSubscriber with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -362,7 +362,7 @@
                 statsd.unsetBroadcastSubscriber(configId, subscriberId, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
+            Log.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -378,7 +378,7 @@
                 return statsd.getRegisteredExperimentIds();
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
+            Log.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -396,7 +396,7 @@
                 return statsd.getMetadata();
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to getMetadata with statsd");
+            Log.e(TAG, "Failed to getMetadata with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -415,7 +415,7 @@
                 return statsd.getData(key, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to getData with statsd");
+            Log.e(TAG, "Failed to getData with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -436,7 +436,7 @@
                 return;
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to addConfiguration with statsd");
+            Log.e(TAG, "Failed to addConfiguration with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -457,7 +457,7 @@
                 return;
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to removeConfiguration with statsd");
+            Log.e(TAG, "Failed to removeConfiguration with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -522,7 +522,7 @@
                 try {
                     mLock.wait(STATSD_TIMEOUT_MILLIS);
                 } catch (InterruptedException e) {
-                    Slog.e(TAG, "wait for statsd interrupted");
+                    Log.e(TAG, "wait for statsd interrupted");
                 }
             }
             return mStatsd;
@@ -578,7 +578,7 @@
             registerAllActiveConfigsChangedOperations(statsd);
             registerAllBroadcastSubscribers(statsd);
         } catch (RemoteException e) {
-            Slog.e(TAG, "StatsManager failed to (re-)register data with statsd");
+            Log.e(TAG, "StatsManager failed to (re-)register data with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/apex/statsd/testing/Android.bp b/apex/statsd/testing/Android.bp
index 22e7301..a9cd0cc 100644
--- a/apex/statsd/testing/Android.bp
+++ b/apex/statsd/testing/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-apex {
+apex_test {
     name: "test_com.android.os.statsd",
     visibility: [
         "//system/apex/tests",
diff --git a/api/current.txt b/api/current.txt
index fb73f2a..826d409 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30780,7 +30780,6 @@
     method public void close();
     method public void continueCall(int) throws android.net.sip.SipException;
     method public void endCall() throws android.net.sip.SipException;
-    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
     method public android.net.sip.SipProfile getLocalProfile();
     method public android.net.sip.SipProfile getPeerProfile();
     method public int getState();
@@ -30791,7 +30790,6 @@
     method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
     method public void sendDtmf(int);
     method public void sendDtmf(int, android.os.Message);
-    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
     method public void setListener(android.net.sip.SipAudioCall.Listener);
     method public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
     method public void setSpeakerMode(boolean);
@@ -30840,7 +30838,6 @@
     method public void close(String) throws android.net.sip.SipException;
     method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
     method public static String getCallId(android.content.Intent);
-    method @NonNull public java.util.List<android.net.sip.SipProfile> getListOfProfiles() throws android.net.sip.SipException;
     method public static String getOfferSessionDescription(android.content.Intent);
     method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
     method public static boolean isApiSupported(android.content.Context);
@@ -30858,11 +30855,6 @@
     method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
     method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
     method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
-    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
-    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
-    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
-    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
     field public static final String EXTRA_CALL_ID = "android:sipCallID";
     field public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
     field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
@@ -30872,7 +30864,6 @@
     method public int describeContents();
     method public String getAuthUserName();
     method public boolean getAutoRegistration();
-    method public int getCallingUid();
     method public String getDisplayName();
     method public String getPassword();
     method public int getPort();
@@ -40426,6 +40417,7 @@
     field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
     field public static final String EXTRA_BIOMETRIC_MINIMUM_STRENGTH_REQUIRED = "android.provider.extra.BIOMETRIC_MINIMUM_STRENGTH_REQUIRED";
     field public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
+    field public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
     field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
     field public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
     field public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID = "android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID";
@@ -47322,7 +47314,7 @@
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
     method public void onUserMobileDataStateChanged(boolean);
     field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
     field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
     field public static final int LISTEN_CALL_STATE = 32; // 0x20
@@ -47335,7 +47327,7 @@
     field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field public static final int LISTEN_NONE = 0; // 0x0
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
     field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
     field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
@@ -60899,7 +60891,7 @@
     method public int getGravity();
     method public float getHorizontalMargin();
     method public float getVerticalMargin();
-    method @Deprecated public android.view.View getView();
+    method @Deprecated @Nullable public android.view.View getView();
     method public int getXOffset();
     method public int getYOffset();
     method public static android.widget.Toast makeText(android.content.Context, CharSequence, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index bf87e55..24936d5 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3862,6 +3862,20 @@
     method @NonNull public android.location.GnssReflectingPlane.Builder setLongitudeDegrees(@FloatRange(from=-180.0F, to=180.0f) double);
   }
 
+  public final class GnssRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isFullTracking();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssRequest> CREATOR;
+  }
+
+  public static final class GnssRequest.Builder {
+    ctor public GnssRequest.Builder();
+    ctor public GnssRequest.Builder(@NonNull android.location.GnssRequest);
+    method @NonNull public android.location.GnssRequest build();
+    method @NonNull public android.location.GnssRequest.Builder setFullTracking(boolean);
+  }
+
   public final class GnssSingleSatCorrection implements android.os.Parcelable {
     method public int describeContents();
     method @FloatRange(from=0.0f, fromInclusive=false) public float getCarrierFrequencyHz();
@@ -4135,6 +4149,7 @@
     method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.LOCATION_HARDWARE}) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -5742,7 +5757,6 @@
     method public int getFreqOffset();
     method public int getHierarchy();
     method @NonNull public boolean[] getLayerErrors();
-    method public int getLberCn();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
@@ -5754,37 +5768,32 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
-    method public int getVberCn();
-    method public int getXerCn();
     method public boolean isDemodLocked();
     method public boolean isEwbs();
     method public boolean isLnaOn();
     method public boolean isRfLock();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
-    field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 24; // 0x18
+    field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
     field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0
     field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd
     field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8
-    field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 21; // 0x15
-    field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 22; // 0x16
+    field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12
+    field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
     field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10
-    field public static final int FRONTEND_STATUS_TYPE_LBER_CN = 18; // 0x12
     field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf
     field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb
-    field public static final int FRONTEND_STATUS_TYPE_MER = 20; // 0x14
+    field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11
     field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9
     field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3
     field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc
     field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4
-    field public static final int FRONTEND_STATUS_TYPE_RF_LOCK = 23; // 0x17
+    field public static final int FRONTEND_STATUS_TYPE_RF_LOCK = 20; // 0x14
     field public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY = 5; // 0x5
     field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
     field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
     field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
     field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
-    field public static final int FRONTEND_STATUS_TYPE_VBER_CN = 17; // 0x11
-    field public static final int FRONTEND_STATUS_TYPE_XER_CN = 19; // 0x13
   }
 
   public static class FrontendStatus.Atsc3PlpInfo {
@@ -7206,6 +7215,28 @@
 
 }
 
+package android.net.sip {
+
+  public class SipAudioCall {
+    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
+    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
+  }
+
+  public class SipManager {
+    method @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
+    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
+    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
+    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
+    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
+    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
+  }
+
+  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method public int getCallingUid();
+  }
+
+}
+
 package android.net.util {
 
   public final class SocketUtils {
diff --git a/api/test-current.txt b/api/test-current.txt
index 826c275..e352cb6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -923,6 +923,7 @@
     field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
+    field public static final int MODULE_APEX_NAME = 1; // 0x1
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
     field public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
   }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 70f309a..956fd29 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -44,12 +44,8 @@
 
 cc_defaults {
     name: "statsd_defaults",
-    aidl: {
-        include_dirs: ["frameworks/base/core/java"],
-    },
 
     srcs: [
-        ":statsd_aidl",
         "src/active_config_list.proto",
         "src/anomaly/AlarmMonitor.cpp",
         "src/anomaly/AlarmTracker.cpp",
@@ -130,8 +126,8 @@
         "libbinder",
         "libincident",
         "liblog",
-        "libservices",
         "libstatssocket",
+        "statsd-aidl-cpp",
     ],
 }
 
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 5b75b97..6b9d0e4 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -26,6 +26,86 @@
 using std::string;
 using std::vector;
 
+// These constants must be kept in sync with those in StatsDimensionsValue.java
+const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
+const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
+const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4;
+// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
+// unused -- statsd does not correctly support bool types)
+const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
+const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
+
+/**
+ * Recursive helper function that populates a parent StatsDimensionsValueParcel
+ * with children StatsDimensionsValueParcels.
+ *
+ * \param dims vector of FieldValues stored by HashableDimensionKey
+ * \param index positions in dims vector to start reading children from
+ * \param depth level of parent parcel in the full StatsDimensionsValueParcel
+ * tree
+ */
+static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel &parentParcel,
+                                                const vector<FieldValue>& dims, size_t& index,
+                                                int depth, int prefix) {
+    while (index < dims.size()) {
+        const FieldValue& dim = dims[index];
+        int fieldDepth = dim.mField.getDepth();
+        int fieldPrefix = dim.mField.getPrefix(depth);
+        StatsDimensionsValueParcel childParcel;
+        childParcel.field = dim.mField.getPosAtDepth(depth);
+        if (depth > 2) {
+            ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
+            return;
+        }
+        if (depth == fieldDepth && prefix == fieldPrefix) {
+            switch (dim.mValue.getType()) {
+                case INT:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
+                    childParcel.intValue = dim.mValue.int_value;
+                    break;
+                case LONG:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
+                    childParcel.longValue = dim.mValue.long_value;
+                    break;
+                case FLOAT:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
+                    childParcel.floatValue = dim.mValue.float_value;
+                    break;
+                case STRING:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
+                    childParcel.stringValue = String16(dim.mValue.str_value.c_str());
+                    break;
+                default:
+                    ALOGE("Encountered FieldValue with unsupported value type.");
+                    break;
+            }
+            index++;
+            parentParcel.tupleValue.push_back(childParcel);
+        } else if (fieldDepth > depth && fieldPrefix == prefix) {
+            childParcel.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+            populateStatsDimensionsValueParcelChildren(childParcel, dims, index, depth + 1,
+                                                       dim.mField.getPrefix(depth + 1));
+            parentParcel.tupleValue.push_back(childParcel);
+        } else {
+            return;
+        }
+    }
+}
+
+StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
+    StatsDimensionsValueParcel parcel;
+    if (mValues.size() == 0) {
+        return parcel;
+    }
+
+    parcel.field = mValues[0].mField.getTag();
+    parcel.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+
+    size_t index = 0;
+    populateStatsDimensionsValueParcelChildren(parcel, mValues, index, /*depth=*/0, /*prefix=*/0);
+    return parcel;
+}
+
 android::hash_t hashDimension(const HashableDimensionKey& value) {
     android::hash_t hash = 0;
     for (const auto& fieldValue : value.getValues()) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 654e135..4adcf96 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,10 +16,11 @@
 
 #pragma once
 
+#include <android/os/StatsDimensionsValueParcel.h>
 #include <utils/JenkinsHash.h>
 #include <vector>
-#include "FieldValue.h"
 #include "android-base/stringprintf.h"
+#include "FieldValue.h"
 #include "logd/LogEvent.h"
 
 namespace android {
@@ -69,6 +70,8 @@
         return nullptr;
     }
 
+    StatsDimensionsValueParcel toStatsDimensionsValueParcel() const;
+
     std::string toString() const;
 
     bool operator!=(const HashableDimensionKey& that) const;
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index bde15a5..6e7f081 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -196,17 +196,27 @@
         !checkPermissionForIds(kPermissionUsage, pid, uid)) {
         return;
     }
-    status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR, err4 = NO_ERROR;
-    string trainName = string(event->GetString(1 /*train name field id*/, &err));
-    int64_t trainVersionCode = event->GetLong(2 /*train version field id*/, &err2);
-    int32_t state = int32_t(event->GetLong(6 /*state field id*/, &err3));
+    // The Get* functions don't modify the status on success, they only write in
+    // failure statuses, so we can use one status variable for all calls then
+    // check if it is no longer NO_ERROR.
+    status_t err = NO_ERROR;
+    InstallTrainInfo trainInfo;
+    trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err));
+    trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err);
+    trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err);
+    trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err);
+    trainInfo.requiresLowLatencyMonitor =
+            event->GetBool(5 /*requires low latency monitor field id*/, &err);
+    trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err));
 #ifdef NEW_ENCODING_SCHEME
     std::vector<uint8_t> trainExperimentIdBytes =
-        event->GetStorage(7 /*experiment ids field id*/, &err4);
+            event->GetStorage(7 /*experiment ids field id*/, &err);
 #else
-    string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err4);
+    string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err);
 #endif
-    if (err != NO_ERROR || err2 != NO_ERROR || err3 != NO_ERROR || err4 != NO_ERROR) {
+    bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err);
+
+    if (err != NO_ERROR) {
         ALOGE("Failed to parse fields in binary push state changed log event");
         return;
     }
@@ -220,83 +230,154 @@
         ALOGE("Failed to parse experimentids in binary push state changed.");
         return;
     }
-    vector<int64_t> experimentIdVector = {trainExperimentIds.experiment_id().begin(),
-                                          trainExperimentIds.experiment_id().end()};
+    trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(),
+                               trainExperimentIds.experiment_id().end()};
+
     // Update the train info on disk and get any data the logevent is missing.
-    getAndUpdateTrainInfoOnDisk(
-        state, &trainVersionCode, &trainName, &experimentIdVector);
+    getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo);
 
     std::vector<uint8_t> trainExperimentIdProto;
-    writeExperimentIdsToProto(experimentIdVector, &trainExperimentIdProto);
+    writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto);
     int32_t userId = multiuser_get_user_id(uid);
 
-    event->updateValue(1 /*train name field id*/, trainName, STRING);
-    event->updateValue(2 /*train version field id*/, trainVersionCode, LONG);
+    event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG);
 #ifdef NEW_ENCODING_SCHEME
     event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
 #else
     event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STRING);
 #endif
     event->updateValue(8 /*user id field id*/, userId, INT);
+
+    if (is_rollback) {
+        int bit = trainInfo.requiresStaging ? 1 : 0;
+        event->updateValue(3 /*requires staging field id*/, bit, INT);
+        bit = trainInfo.rollbackEnabled ? 1 : 0;
+        event->updateValue(4 /*rollback enabled field id*/, bit, INT);
+        bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0;
+        event->updateValue(5 /*requires low latency monitor field id*/, bit, INT);
+    }
 }
 
-void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(int32_t state,
-                                         int64_t* trainVersionCode,
-                                         string* trainName,
-                                         std::vector<int64_t>* experimentIds) {
+void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback,
+                                                    InstallTrainInfo* trainInfo) {
+    // If the train name is empty, we don't know which train to attribute the
+    // event to, so return early.
+    if (trainInfo->trainName.empty()) {
+        return;
+    }
     bool readTrainInfoSuccess = false;
     InstallTrainInfo trainInfoOnDisk;
-    readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
+    readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk);
 
     bool resetExperimentIds = false;
     if (readTrainInfoSuccess) {
         // Keep the old train version if we received an empty version.
-        if (*trainVersionCode == -1) {
-            *trainVersionCode = trainInfoOnDisk.trainVersionCode;
-        } else if (*trainVersionCode != trainInfoOnDisk.trainVersionCode) {
-        // Reset experiment ids if we receive a new non-empty train version.
-            resetExperimentIds = true;
-        }
-
-        // Keep the old train name if we received an empty train name.
-        if (trainName->size() == 0) {
-            *trainName = trainInfoOnDisk.trainName;
-        } else if (*trainName != trainInfoOnDisk.trainName) {
-            // Reset experiment ids if we received a new valid train name.
+        if (trainInfo->trainVersionCode == -1) {
+            trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode;
+        } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) {
+            // Reset experiment ids if we receive a new non-empty train version.
             resetExperimentIds = true;
         }
 
         // Reset if we received a different experiment id.
-        if (!experimentIds->empty() &&
-                (trainInfoOnDisk.experimentIds.empty() ||
-                 experimentIds->at(0) != trainInfoOnDisk.experimentIds[0])) {
+        if (!trainInfo->experimentIds.empty() &&
+            (trainInfoOnDisk.experimentIds.empty() ||
+             trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) {
             resetExperimentIds = true;
         }
     }
 
     // Find the right experiment IDs
-    if (!resetExperimentIds && readTrainInfoSuccess) {
-        *experimentIds = trainInfoOnDisk.experimentIds;
+    if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) {
+        trainInfo->experimentIds = trainInfoOnDisk.experimentIds;
     }
 
-    if (!experimentIds->empty()) {
-        int64_t firstId = experimentIds->at(0);
-        switch (state) {
+    if (!trainInfo->experimentIds.empty()) {
+        int64_t firstId = trainInfo->experimentIds.at(0);
+        switch (trainInfo->status) {
             case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
-                experimentIds->push_back(firstId + 1);
+                trainInfo->experimentIds.push_back(firstId + 1);
                 break;
             case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
-                experimentIds->push_back(firstId + 2);
+                trainInfo->experimentIds.push_back(firstId + 2);
                 break;
             case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
-                experimentIds->push_back(firstId + 3);
+                trainInfo->experimentIds.push_back(firstId + 3);
                 break;
         }
     }
 
-    StorageManager::writeTrainInfo(*trainVersionCode, *trainName, state, *experimentIds);
+    if (is_rollback) {
+        trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging;
+        trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled;
+        trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor;
+    }
+
+    StorageManager::writeTrainInfo(*trainInfo);
 }
 
+void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) {
+    pid_t pid = event->GetPid();
+    uid_t uid = event->GetUid();
+    if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+        !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+        return;
+    }
+    // The Get* functions don't modify the status on success, they only write in
+    // failure statuses, so we can use one status variable for all calls then
+    // check if it is no longer NO_ERROR.
+    status_t err = NO_ERROR;
+    int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err));
+    string packageName = string(event->GetString(2 /*package name field id*/, &err));
+
+    if (err != NO_ERROR) {
+        ALOGE("Failed to parse fields in watchdog rollback occurred log event");
+        return;
+    }
+
+    vector<int64_t> experimentIds =
+        processWatchdogRollbackOccurred(rollbackType, packageName);
+    vector<uint8_t> experimentIdProto;
+    writeExperimentIdsToProto(experimentIds, &experimentIdProto);
+
+#ifdef NEW_ENCODING_SCHEME
+    event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE);
+#else
+    event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STRING);
+#endif
+}
+
+vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+                                                                    const string& packageNameIn) {
+    // If the package name is empty, we can't attribute it to any train, so
+    // return early.
+    if (packageNameIn.empty()) {
+      return vector<int64_t>();
+    }
+    bool readTrainInfoSuccess = false;
+    InstallTrainInfo trainInfoOnDisk;
+    readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk);
+
+    if (!readTrainInfoSuccess) {
+        return vector<int64_t>();
+    }
+
+    if (trainInfoOnDisk.experimentIds.empty()) {
+        return vector<int64_t>();
+    }
+    switch (rollbackTypeIn) {
+        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+            trainInfoOnDisk.experimentIds.push_back(trainInfoOnDisk.experimentIds[0] + 4);
+            StorageManager::writeTrainInfo(trainInfoOnDisk);
+            break;
+        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+            trainInfoOnDisk.experimentIds.push_back(trainInfoOnDisk.experimentIds[0] + 5);
+            StorageManager::writeTrainInfo(trainInfoOnDisk);
+            break;
+    }
+
+    return trainInfoOnDisk.experimentIds;
+}
 
 void StatsLogProcessor::resetConfigs() {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -321,7 +402,13 @@
     // Hard-coded logic to update train info on disk and fill in any information
     // this log event may be missing.
     if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) {
-      onBinaryPushStateChangedEventLocked(event);
+        onBinaryPushStateChangedEventLocked(event);
+    }
+
+    // Hard-coded logic to update experiment ids on disk for certain rollback
+    // types and fill the rollback atom with experiment ids
+    if (event->GetTagId() == android::util::WATCHDOG_ROLLBACK_OCCURRED) {
+        onWatchdogRollbackOccurredLocked(event);
     }
 
 #ifdef VERY_VERBOSE_PRINTING
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c49f2e0..42e5676 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest_prod.h>
 #include "config/ConfigListener.h"
+#include "logd/LogEvent.h"
 #include "metrics/MetricsManager.h"
 #include "packages/UidMap.h"
 #include "external/StatsPullerManager.h"
@@ -199,10 +200,18 @@
     // Handler over the binary push state changed event.
     void onBinaryPushStateChangedEventLocked(LogEvent* event);
 
+    // Handler over the watchdog rollback occurred event.
+    void onWatchdogRollbackOccurredLocked(LogEvent* event);
+
     // Updates train info on disk based on binary push state changed info and
     // write disk info into parameters.
-    void getAndUpdateTrainInfoOnDisk(int32_t state, int64_t* trainVersionCode,
-                                     string* trainName, std::vector<int64_t>* experimentIds);
+    void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
+
+    // Gets experiment ids on disk for associated train and updates them
+    // depending on rollback type. Then writes them back to disk and returns
+    // them.
+    std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+                                                          const string& packageName);
 
     // Reset all configs.
     void resetConfigsLocked(const int64_t timestampNs);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index a06e59c..168833f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1293,81 +1293,24 @@
     return Status::ok();
 }
 
-Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn,
-                                                      const android::String16& packageNameIn,
-                                                      const int64_t packageVersionCodeIn,
-                                                      const int32_t rollbackReasonIn,
-                                                      const android::String16&
-                                                       failingPackageNameIn) {
-    // Note: We skip the usage stats op check here since we do not have a package name.
-    // This is ok since we are overloading the usage_stats permission.
-    // This method only sends data, it does not receive it.
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-    // Root, system, and shell always have access
-    if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
-        // Caller must be granted these permissions
-        if (!checkPermission(kPermissionDump)) {
-            return exception(binder::Status::EX_SECURITY,
-                             StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
-                                          kPermissionDump));
-        }
-        if (!checkPermission(kPermissionUsage)) {
-            return exception(binder::Status::EX_SECURITY,
-                             StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
-                                          kPermissionUsage));
-        }
-    }
-
-    android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED,
-            rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn,
-            rollbackReasonIn, String8(failingPackageNameIn).string());
-
-    // Fast return to save disk read.
-    if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS
-            && rollbackTypeIn !=
-                    android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE) {
-        return Status::ok();
-    }
-
-    bool readTrainInfoSuccess = false;
-    InstallTrainInfo trainInfoOnDisk;
-    readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
-    if (!readTrainInfoSuccess) {
-        return Status::ok();
-    }
-    std::vector<int64_t> experimentIds = trainInfoOnDisk.experimentIds;
-    if (experimentIds.empty()) {
-        return Status::ok();
-    }
-    switch (rollbackTypeIn) {
-        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
-            experimentIds.push_back(experimentIds[0] + 4);
-            break;
-        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
-            experimentIds.push_back(experimentIds[0] + 5);
-            break;
-    }
-    StorageManager::writeTrainInfo(trainInfoOnDisk.trainVersionCode, trainInfoOnDisk.trainName,
-            trainInfoOnDisk.status, experimentIds);
-    return Status::ok();
-}
-
 Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
     ENFORCE_UID(AID_SYSTEM);
     // TODO: add verifier permission
 
+    experimentIdsOut->clear();
     // Read the latest train info
-    InstallTrainInfo trainInfo;
-    if (!StorageManager::readTrainInfo(trainInfo)) {
+    vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo();
+    if (trainInfoList.empty()) {
         // No train info means no experiment IDs, return an empty list
-        experimentIdsOut->clear();
         return Status::ok();
     }
 
     // Copy the experiment IDs to the out vector
-    experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end());
+    for (InstallTrainInfo& trainInfo : trainInfoList) {
+        experimentIdsOut->insert(experimentIdsOut->end(),
+                                 trainInfo.experimentIds.begin(),
+                                 trainInfo.experimentIds.end());
+    }
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 82a5a53..0527d43 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -187,16 +187,6 @@
     virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
 
     /**
-     * Binder call to log WatchdogRollbackOccurred atom.
-     */
-    virtual Status sendWatchdogRollbackOccurredAtom(
-            const int32_t rollbackTypeIn,
-            const android::String16& packageNameIn,
-            const int64_t packageVersionCodeIn,
-            const int32_t rollbackReasonIn,
-            const android::String16& failingPackageNameIn) override;
-
-    /**
      * Binder call to get registered experiment IDs.
      */
     virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 71afc32..5e2dbf3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1874,6 +1874,8 @@
     // Set by RollbackPackageHealthObserver to be the package that is failing when a rollback
     // is initiated. Empty if the package is unknown.
     optional string failing_package_name = 5;
+
+    optional TrainExperimentIds experiment_ids = 6 [(log_mode) = MODE_BYTES];
 }
 
 /**
@@ -3770,6 +3772,8 @@
     // user id
     optional int32 user_id = 8;
     optional int32 reason = 9;
+    // Whether or not this is a rollback event
+    optional bool is_rollback = 10;
 }
 
 /* Test atom, is not logged anywhere */
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
index 9d09242..a7d8d4e 100644
--- a/cmds/statsd/src/external/TrainInfoPuller.cpp
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -37,14 +37,16 @@
 }
 
 bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
-    InstallTrainInfo trainInfo;
-    bool ret = StorageManager::readTrainInfo(trainInfo);
-    if (!ret) {
-        ALOGW("Failed to read train info.");
-        return false;
+    vector<InstallTrainInfo> trainInfoList =
+        StorageManager::readAllTrainInfo();
+    if (trainInfoList.empty()) {
+        ALOGW("Train info was empty.");
+        return true;
     }
-    auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
-    data->push_back(event);
+    for (InstallTrainInfo& trainInfo : trainInfoList) {
+        auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+        data->push_back(event);
+    }
     return true;
 }
 
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 5509c09..a67bef4 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -56,6 +56,9 @@
     std::string trainName;
     int32_t status;
     std::vector<int64_t> experimentIds;
+    bool requiresStaging;
+    bool rollbackEnabled;
+    bool requiresLowLatencyMonitor;
 };
 
 /**
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 507297c..1bac19e 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -44,7 +44,7 @@
 #define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
 
 // Magic word at the start of the train info file, change this if changing the file format
-const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;
+const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
 
 // for ConfigMetricsReportList
 const int FIELD_ID_REPORTS = 2;
@@ -75,6 +75,29 @@
                         (long long)id);
 }
 
+static const char* findTrainInfoFileNameLocked(const string& trainName) {
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+    if (dir == NULL) {
+        VLOG("Path %s does not exist", TRAIN_INFO_DIR);
+        return nullptr;
+    }
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* fileName = de->d_name;
+        if (fileName[0] == '.') continue;
+
+        size_t fileNameLength = strlen(fileName);
+        if (fileNameLength >= trainName.length()) {
+            if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
+                             trainName.length())) {
+                return fileName;
+            }
+        }
+    }
+
+    return nullptr;
+}
+
 // Returns array of int64_t which contains timestamp in seconds, uid,
 // configID and whether the file is a local history file.
 static void parseFileName(char* name, FileName* output) {
@@ -123,20 +146,25 @@
     close(fd);
 }
 
-bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
-                                    int32_t status, const std::vector<int64_t>& experimentIds) {
+bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
 
-    deleteAllFiles(TRAIN_INFO_DIR);
+    if (trainInfo.trainName.empty()) {
+      return false;
+    }
+    deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
 
-    int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+    std::string fileName =
+            StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
+                         trainInfo.trainName.c_str());
+
+    int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
     if (fd == -1) {
-        VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
+        VLOG("Attempt to access %s but failed", fileName.c_str());
         return false;
     }
 
     size_t result;
-
     // Write the magic word
     result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
     if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
@@ -146,8 +174,8 @@
     }
 
     // Write the train version
-    const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
-    result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
+    const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
+    result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
     if (result != trainVersionCodeByteCount) {
         VLOG("Failed to wrtie train version code");
         close(fd);
@@ -155,7 +183,7 @@
     }
 
     // Write # of bytes in trainName to file
-    const size_t trainNameSize = trainName.size();
+    const size_t trainNameSize = trainInfo.trainName.size();
     const size_t trainNameSizeByteCount = sizeof(trainNameSize);
     result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
     if (result != trainNameSizeByteCount) {
@@ -165,7 +193,7 @@
     }
 
     // Write trainName to file
-    result = write(fd, trainName.c_str(), trainNameSize);
+    result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
     if (result != trainNameSize) {
         VLOG("Failed to write train name");
         close(fd);
@@ -173,8 +201,8 @@
     }
 
     // Write status to file
-    const size_t statusByteCount = sizeof(status);
-    result = write(fd, (uint8_t*)&status, statusByteCount);
+    const size_t statusByteCount = sizeof(trainInfo.status);
+    result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
     if (result != statusByteCount) {
         VLOG("Failed to write status");
         close(fd);
@@ -182,7 +210,7 @@
     }
 
     // Write experiment id count to file.
-    const size_t experimentIdsCount = experimentIds.size();
+    const size_t experimentIdsCount = trainInfo.experimentIds.size();
     const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
     result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
     if (result != experimentIdsCountByteCount) {
@@ -193,7 +221,7 @@
 
     // Write experimentIds to file
     for (size_t i = 0; i < experimentIdsCount; i++) {
-        const int64_t experimentId = experimentIds[i];
+        const int64_t experimentId = trainInfo.experimentIds[i];
         const size_t experimentIdByteCount = sizeof(experimentId);
         result = write(fd, &experimentId, experimentIdByteCount);
         if (result == experimentIdByteCount) {
@@ -205,23 +233,47 @@
         }
     }
 
-    result = fchown(fd, AID_STATSD, AID_STATSD);
-    if (result) {
-        VLOG("Failed to chown train info file to statsd");
-        close(fd);
-        return false;
+    // Write bools to file
+    const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+    result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
+    if (result != boolByteCount) {
+      VLOG("Failed to write requires staging");
+      close(fd);
+      return false;
+    }
+
+    result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
+    if (result != boolByteCount) {
+      VLOG("Failed to write rollback enabled");
+      close(fd);
+      return false;
+    }
+
+    result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
+    if (result != boolByteCount) {
+      VLOG("Failed to write requires log latency monitor");
+      close(fd);
+      return false;
     }
 
     close(fd);
     return true;
 }
 
-bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+    return readTrainInfoLocked(trainName, trainInfo);
+}
 
-    int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
+bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
+    trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
+    const char* fileName = findTrainInfoFileNameLocked(trainName);
+    if (fileName == nullptr) {
+        return false;
+    }
+    int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName).c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
-        VLOG("Failed to open train-info.bin");
+        VLOG("Failed to open %s", fileName);
         return false;
     }
 
@@ -297,6 +349,29 @@
         trainInfo.experimentIds.push_back(experimentId);
     }
 
+    // Read bools
+    const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+    result = read(fd, &trainInfo.requiresStaging, boolByteCount);
+    if (result != boolByteCount) {
+        VLOG("Failed to read requires requires staging from train info file");
+        close(fd);
+        return false;
+    }
+
+    result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
+    if (result != boolByteCount) {
+        VLOG("Failed to read requires rollback enabled from train info file");
+        close(fd);
+        return false;
+    }
+
+    result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
+    if (result != boolByteCount) {
+        VLOG("Failed to read requires requires low latency monitor from train info file");
+        close(fd);
+        return false;
+    }
+
     // Expect to be at EOF.
     char c;
     result = read(fd, &c, 1);
@@ -311,6 +386,32 @@
     return true;
 }
 
+vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
+    std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+    vector<InstallTrainInfo> trainInfoList;
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+    if (dir == NULL) {
+        VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+        return trainInfoList;
+    }
+
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* name = de->d_name;
+        if (name[0] == '.') {
+            continue;
+        }
+
+        InstallTrainInfo trainInfo;
+        bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
+        if (!readSuccess) {
+            continue;
+        }
+        trainInfoList.push_back(trainInfo);
+    }
+    return trainInfoList;
+}
+
 void StorageManager::deleteFile(const char* file) {
     if (remove(file) != 0) {
         VLOG("Attempt to delete %s but is not found", file);
@@ -574,7 +675,7 @@
     });
 }
 
-void StorageManager::trimToFit(const char* path) {
+void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
     if (dir == NULL) {
         VLOG("Path %s does not exist", path);
@@ -589,7 +690,12 @@
         if (name[0] == '.') continue;
 
         FileName output;
-        parseFileName(name, &output);
+        if (parseTimestampOnly) {
+            output.mTimestampSec = StrToInt64(strtok(name, "_"));
+            output.mIsHistory = false;
+        } else {
+            parseFileName(name, &output);
+        }
         if (output.mTimestampSec == -1) continue;
         string file_name = output.getFullFileName(path);
 
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 69b41c2..d59046d 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -52,13 +52,22 @@
     /**
      * Writes train info.
      */
-    static bool writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
-                               int32_t status, const std::vector<int64_t>& experimentIds);
+    static bool writeTrainInfo(const InstallTrainInfo& trainInfo);
 
     /**
      * Reads train info.
      */
-    static bool readTrainInfo(InstallTrainInfo& trainInfo);
+    static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+    /**
+     * Reads train info assuming lock is obtained.
+     */
+    static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+    /**
+     * Reads all train info and returns a vector of train info.
+     */
+    static vector<InstallTrainInfo> readAllTrainInfo();
 
     /**
      * Reads the file content to the buffer.
@@ -124,7 +133,7 @@
      * Trims files in the provided directory to limit the total size, number of
      * files, accumulation of outdated files.
      */
-    static void trimToFit(const char* dir);
+    static void trimToFit(const char* dir, bool parseTimestampOnly = false);
 
     /**
      * Returns true if there already exists identical configuration on device.
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 160b57e..8fd6b46 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -121,7 +121,7 @@
             subscription.id(),
             subscription.rule_id(),
             cookies,
-            getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
+            dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
 }
 
 sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
@@ -138,61 +138,6 @@
     return pirMapIt->second;
 }
 
-void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
-                                   int prefix, vector<StatsDimensionsValue>* output) {
-    size_t count = dims.size();
-    while (*index < count) {
-        const auto& dim = dims[*index];
-        const int valueDepth = dim.mField.getDepth();
-        const int valuePrefix = dim.mField.getPrefix(depth);
-        if (valueDepth > 2) {
-            ALOGE("Depth > 2 not supported");
-            return;
-        }
-        if (depth == valueDepth && valuePrefix == prefix) {
-            switch (dim.mValue.getType()) {
-                case INT:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           dim.mValue.int_value));
-                    break;
-                case LONG:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           dim.mValue.long_value));
-                    break;
-                case FLOAT:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           dim.mValue.float_value));
-                    break;
-                case STRING:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           String16(dim.mValue.str_value.c_str())));
-                    break;
-                default:
-                    break;
-            }
-            (*index)++;
-        } else if (valueDepth > depth && valuePrefix == prefix) {
-            vector<StatsDimensionsValue> childOutput;
-            getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1),
-                                          &childOutput);
-            output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput));
-        } else {
-            return;
-        }
-    }
-}
-
-StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) {
-    if (dim.getValues().size() == 0) {
-        return StatsDimensionsValue();
-    }
-
-    vector<StatsDimensionsValue> fields;
-    size_t index = 0;
-    getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields);
-    return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields);
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 087a1b8..42599f5 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -22,7 +22,6 @@
 
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // subscription
-#include "android/os/StatsDimensionsValue.h"
 #include "HashableDimensionKey.h"
 
 #include <mutex>
@@ -70,8 +69,6 @@
 
     sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
 
-    static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
-
 private:
     SubscriberReporter() {};
 
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index f4a59ed..9e69d97 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -290,33 +290,34 @@
     }
 }
 
-TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
-    HashableDimensionKey dim;
-
-    int pos1[] = {1, 1, 1};
-    int pos2[] = {1, 1, 2};
-    int pos3[] = {1, 1, 3};
-    int pos4[] = {2, 0, 0};
-
-    Field field1(10, pos1, 2);
-    Field field2(10, pos2, 2);
-    Field field3(10, pos3, 2);
-    Field field4(10, pos4, 0);
-
-    Value value1((int32_t)10025);
-    Value value2("tag");
-    Value value3((int32_t)987654);
-    Value value4((int32_t)99999);
-
-    dim.addValue(FieldValue(field1, value1));
-    dim.addValue(FieldValue(field2, value2));
-    dim.addValue(FieldValue(field3, value3));
-    dim.addValue(FieldValue(field4, value4));
-
-    SubscriberReporter::getStatsDimensionsValue(dim);
-    // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
-    // have any read api.
-}
+//TODO(b/149050405) Update this test for StatsDimensionValueParcel
+//TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
+//    HashableDimensionKey dim;
+//
+//    int pos1[] = {1, 1, 1};
+//    int pos2[] = {1, 1, 2};
+//    int pos3[] = {1, 1, 3};
+//    int pos4[] = {2, 0, 0};
+//
+//    Field field1(10, pos1, 2);
+//    Field field2(10, pos2, 2);
+//    Field field3(10, pos3, 2);
+//    Field field4(10, pos4, 0);
+//
+//    Value value1((int32_t)10025);
+//    Value value2("tag");
+//    Value value3((int32_t)987654);
+//    Value value4((int32_t)99999);
+//
+//    dim.addValue(FieldValue(field1, value1));
+//    dim.addValue(FieldValue(field2, value2));
+//    dim.addValue(FieldValue(field3, value3));
+//    dim.addValue(FieldValue(field4, value4));
+//
+//    SubscriberReporter::getStatsDimensionsValue(dim);
+//    // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
+//    // have any read api.
+//}
 
 TEST(AtomMatcherTest, TestWriteDimensionToProto) {
     HashableDimensionKey dim;
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index b91e5a0..27a86e42 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -40,40 +40,12 @@
 
     bool result;
 
-    result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
-                                            trainInfo.status, trainInfo.experimentIds);
+    result = StorageManager::writeTrainInfo(trainInfo);
 
     EXPECT_TRUE(result);
 
     InstallTrainInfo trainInfoResult;
-    result = StorageManager::readTrainInfo(trainInfoResult);
-    EXPECT_TRUE(result);
-
-    EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
-    EXPECT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
-    EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
-    EXPECT_EQ(trainInfo.status, trainInfoResult.status);
-    EXPECT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
-    EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
-}
-
-TEST(StorageManagerTest, TrainInfoReadWriteEmptyTrainNameTest) {
-    InstallTrainInfo trainInfo;
-    trainInfo.trainVersionCode = 12345;
-    trainInfo.trainName = "";
-    trainInfo.status = 1;
-    const char* expIds = "test_ids";
-    trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
-
-    bool result;
-
-    result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
-                                            trainInfo.status, trainInfo.experimentIds);
-
-    EXPECT_TRUE(result);
-
-    InstallTrainInfo trainInfoResult;
-    result = StorageManager::readTrainInfo(trainInfoResult);
+    result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
     EXPECT_TRUE(result);
 
     EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
@@ -94,13 +66,12 @@
 
     bool result;
 
-    result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
-                                            trainInfo.status, trainInfo.experimentIds);
+    result = StorageManager::writeTrainInfo(trainInfo);
 
     EXPECT_TRUE(result);
 
     InstallTrainInfo trainInfoResult;
-    result = StorageManager::readTrainInfo(trainInfoResult);
+    result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
     EXPECT_TRUE(result);
 
     EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 642f51b..2319dd2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5810,9 +5810,9 @@
                 intent.prepareToLeaveProcess(this);
                 result = ActivityTaskManager.getService()
                     .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
-                            intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken,
-                            mEmbeddedID, requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED,
-                            null, options);
+                            getFeatureId(), intent,
+                            intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID,
+                            requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, options);
             } catch (RemoteException e) {
                 // Empty
             }
@@ -6606,8 +6606,8 @@
         try {
             data.prepareToLeaveProcess(this);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getFeatureId(),
                         mParent == null ? mToken : mParent.mToken,
                         mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
                         getUserId());
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7ee4405..2838ad8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4316,8 +4316,8 @@
      */
     public static void broadcastStickyIntent(Intent intent, int appOp, int userId) {
         try {
-            getService().broadcastIntent(
-                    null, intent, null, null, Activity.RESULT_OK, null, null,
+            getService().broadcastIntentWithFeature(
+                    null, null, intent, null, null, Activity.RESULT_OK, null, null,
                     null /*permission*/, appOp, null, false, true, userId);
         } catch (RemoteException ex) {
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c60f7bd..ec11043 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -269,13 +269,16 @@
 
     public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
             long duration, String tag);
-    public abstract int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
-            int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle resultExtras, String requiredPermission,
-            Bundle bOptions, boolean serialized, boolean sticky, @UserIdInt int userId,
-            boolean allowBackgroundActivityStarts);
+
+    public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId,
+            int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+            IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
+            String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
+            @UserIdInt int userId, boolean allowBackgroundActivityStarts);
+
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
-            String resolvedType, boolean fgRequired, String callingPackage, @UserIdInt int userId,
+            String resolvedType, boolean fgRequired, String callingPackage,
+            @Nullable String callingFeatureId, @UserIdInt int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
 
     public abstract void disconnectActivityFromServices(Object connectionHolder);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6ef99a3..fc37af9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -3916,7 +3916,10 @@
         }
 
         /**
-         * The features that have been used when checking the op
+         * The features that have been used when checking the op keyed by id of the feature.
+         *
+         * @see Context#createFeatureContext(String)
+         * @see #noteOp(String, int, String, String, String)
          */
         @DataClass.Generated.Member
         public @NonNull Map<String,OpFeatureEntry> getFeatures() {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 57cd894..6b5bfda 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1020,7 +1020,7 @@
     public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
         try {
             ActivityTaskManager.getService().startActivityAsUser(
-                mMainThread.getApplicationThread(), getBasePackageName(), intent,
+                mMainThread.getApplicationThread(), getBasePackageName(), getFeatureId(), intent,
                 intent.resolveTypeIfNeeded(getContentResolver()),
                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
                 user.getIdentifier());
@@ -1102,8 +1102,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                     getUserId());
         } catch (RemoteException e) {
@@ -1119,8 +1119,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, false, false, getUserId());
         } catch (RemoteException e) {
@@ -1134,8 +1134,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, false, false, getUserId());
         } catch (RemoteException e) {
@@ -1149,8 +1149,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, false, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1166,8 +1166,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     options, false, false, getUserId());
         } catch (RemoteException e) {
@@ -1183,8 +1183,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                     getUserId());
         } catch (RemoteException e) {
@@ -1200,8 +1200,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, true, false, getUserId());
         } catch (RemoteException e) {
@@ -1263,8 +1263,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions, appOp,
                     options, true, false, getUserId());
         } catch (RemoteException e) {
@@ -1277,9 +1277,10 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(),
-                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
-                    AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
+                    user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1299,8 +1300,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     options, false, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1316,8 +1317,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                     user.getIdentifier());
         } catch (RemoteException e) {
@@ -1367,8 +1368,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions,
                     appOp, options, true, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1408,8 +1409,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                 getUserId());
         } catch (RemoteException e) {
@@ -1444,8 +1445,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
                     AppOpsManager.OP_NONE, null, true, true, getUserId());
         } catch (RemoteException e) {
@@ -1476,8 +1477,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                     user.getIdentifier());
         } catch (RemoteException e) {
@@ -1491,8 +1492,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
                 user.getIdentifier());
         } catch (RemoteException e) {
@@ -1526,8 +1527,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
                     AppOpsManager.OP_NONE, null, true, true, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1612,9 +1613,9 @@
             }
         }
         try {
-            final Intent intent = ActivityManager.getService().registerReceiver(
-                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
-                    broadcastPermission, userId, flags);
+            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
+                    mMainThread.getApplicationThread(), mBasePackageName, getFeatureId(), rd,
+                    filter, broadcastPermission, userId, flags);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
                 intent.prepareToEnterProcess();
@@ -1687,9 +1688,9 @@
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
             ComponentName cn = ActivityManager.getService().startService(
-                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
-                            getContentResolver()), requireForeground,
-                            getOpPackageName(), user.getIdentifier());
+                    mMainThread.getApplicationThread(), service,
+                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
+                    getOpPackageName(), getFeatureId(), user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
                     throw new SecurityException(
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index cb6a476..83fa9d7 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -104,25 +104,38 @@
     // Special low-level communication with activity manager.
     void handleApplicationCrash(in IBinder app,
             in ApplicationErrorReport.ParcelableCrashInfo crashInfo);
-    @UnsupportedAppUsage
+    /** @deprecated Use {@link #startActivityWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#startActivity(android.content.Intent)} instead")
     int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
             int flags, in ProfilerInfo profilerInfo, in Bundle options);
+    int startActivityWithFeature(in IApplicationThread caller, in String callingPackage,
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options);
     @UnsupportedAppUsage
     void unhandledBack();
     @UnsupportedAppUsage
     boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)} instead")
     Intent registerReceiver(in IApplicationThread caller, in String callerPackage,
             in IIntentReceiver receiver, in IntentFilter filter,
             in String requiredPermission, int userId, int flags);
+    Intent registerReceiverWithFeature(in IApplicationThread caller, in String callerPackage,
+            in String callingFeatureId, in IIntentReceiver receiver, in IntentFilter filter,
+            in String requiredPermission, int userId, int flags);
     @UnsupportedAppUsage
     void unregisterReceiver(in IIntentReceiver receiver);
-    @UnsupportedAppUsage
+    /** @deprecated Use {@link #broadcastIntentWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#sendBroadcast(android.content.Intent)} instead")
     int broadcastIntent(in IApplicationThread caller, in Intent intent,
             in String resolvedType, in IIntentReceiver resultTo, int resultCode,
             in String resultData, in Bundle map, in String[] requiredPermissions,
             int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);
+    int broadcastIntentWithFeature(in IApplicationThread caller, in String callingFeatureId,
+            in Intent intent, in String resolvedType, in IIntentReceiver resultTo, int resultCode,
+            in String resultData, in Bundle map, in String[] requiredPermissions,
+            int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);
     void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId);
     @UnsupportedAppUsage
     oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
@@ -145,7 +158,8 @@
     boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
-            in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
+            in String resolvedType, boolean requireForeground, in String callingPackage,
+            in String callingFeatureId, int userId);
     @UnsupportedAppUsage
     int stopService(in IApplicationThread caller, in Intent service,
             in String resolvedType, int userId);
@@ -226,10 +240,14 @@
     ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
     @UnsupportedAppUsage
     oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res);
-    @UnsupportedAppUsage
+    /** @deprecated  Use {@link #getIntentSenderWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link PendingIntent#getIntentSender()} instead")
     IIntentSender getIntentSender(int type, in String packageName, in IBinder token,
             in String resultWho, int requestCode, in Intent[] intents, in String[] resolvedTypes,
             int flags, in Bundle options, int userId);
+    IIntentSender getIntentSenderWithFeature(int type, in String packageName, in String featureId,
+            in IBinder token, in String resultWho, int requestCode, in Intent[] intents,
+            in String[] resolvedTypes, int flags, in Bundle options, int userId);
     void cancelIntentSender(in IIntentSender sender);
     String getPackageForIntentSender(in IIntentSender sender);
     void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
@@ -355,11 +373,16 @@
     boolean isIntentSenderAnActivity(in IIntentSender sender);
     boolean isIntentSenderAForegroundService(in IIntentSender sender);
     boolean isIntentSenderABroadcast(in IIntentSender sender);
-    @UnsupportedAppUsage
+    /** @deprecated Use {@link startActivityAsUserWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@code android.content.Context#createContextAsUser(android.os.UserHandle, int)} and {@link android.content.Context#startActivity(android.content.Intent)} instead")
     int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
             in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
             int requestCode, int flags, in ProfilerInfo profilerInfo,
             in Bundle options, int userId);
+    int startActivityAsUserWithFeature(in IApplicationThread caller, in String callingPackage,
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
     @UnsupportedAppUsage
     int stopUser(int userid, boolean force, in IStopUserCallback callback);
     /**
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index be2f144..180507c 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -85,16 +85,17 @@
  * {@hide}
  */
 interface IActivityTaskManager {
-    int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
-            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
+    int startActivity(in IApplicationThread caller, in String callingPackage,
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode,
             int flags, in ProfilerInfo profilerInfo, in Bundle options);
     int startActivities(in IApplicationThread caller, in String callingPackage,
-            in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
-            in Bundle options, int userId);
+            in String callingFeatureId, in Intent[] intents, in String[] resolvedTypes,
+            in IBinder resultTo, in Bundle options, int userId);
     int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int flags, in ProfilerInfo profilerInfo,
-            in Bundle options, int userId);
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
     boolean startNextMatchingActivity(in IBinder callingActivity,
             in Intent intent, in Bundle options);
     int startActivityIntentSender(in IApplicationThread caller,
@@ -102,19 +103,19 @@
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
             int flagsMask, int flagsValues, in Bundle options);
     WaitResult startActivityAndWait(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
-            int userId);
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
     int startActivityWithConfig(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int startFlags, in Configuration newConfig,
-            in Bundle options, int userId);
-    int startVoiceActivity(in String callingPackage, int callingPid, int callingUid,
-            in Intent intent, in String resolvedType, in IVoiceInteractionSession session,
-            in IVoiceInteractor interactor, int flags, in ProfilerInfo profilerInfo,
-            in Bundle options, int userId);
-    int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
-            in Intent intent, in String resolvedType, in Bundle options, int userId);
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int startFlags,
+            in Configuration newConfig, in Bundle options, int userId);
+    int startVoiceActivity(in String callingPackage, in String callingFeatureId, int callingPid,
+            int callingUid, in Intent intent, in String resolvedType,
+            in IVoiceInteractionSession session, in IVoiceInteractor interactor, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
+    int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
+            int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
     void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
             in IRecentsAnimationRunner recentsAnimationRunner);
     int startActivityFromRecents(int taskId, in Bundle options);
diff --git a/core/java/android/app/IAppTask.aidl b/core/java/android/app/IAppTask.aidl
index 3ce7190..f41d705 100644
--- a/core/java/android/app/IAppTask.aidl
+++ b/core/java/android/app/IAppTask.aidl
@@ -27,7 +27,7 @@
     @UnsupportedAppUsage
     ActivityManager.RecentTaskInfo getTaskInfo();
     void moveToFront(in IApplicationThread appThread, in String callingPackage);
-    int startActivity(IBinder whoThread, String callingPackage,
+    int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId,
             in Intent intent, String resolvedType, in Bundle options);
     void setExcludeFromRecents(boolean exclude);
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 16c0910..1d4a1ac 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
     void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+    ParceledListSlice getConversationsForPackage(String pkg, int uid);
     ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
     NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
     NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
@@ -96,7 +97,7 @@
     NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
     NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId);
     void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId);
-    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
+    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, String channelId);
     void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
     ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
@@ -113,6 +114,7 @@
     int getAppsBypassingDndCount(int uid);
     ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
     boolean isPackagePaused(String pkg);
+    void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
 
     void silenceNotificationSound();
 
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 62c905d..18932c6 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1721,7 +1721,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = ActivityTaskManager.getService()
-                .startActivity(whoThread, who.getBasePackageName(), intent,
+                .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target != null ? target.mEmbeddedID : null,
                         requestCode, 0, null, options);
@@ -1794,8 +1794,8 @@
                 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
             }
             int result = ActivityTaskManager.getService()
-                .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
-                        token, options, userId);
+                .startActivities(whoThread, who.getBasePackageName(), who.getFeatureId(), intents,
+                        resolvedTypes, token, options, userId);
             checkStartActivityResult(result, intents[0]);
             return result;
         } catch (RemoteException e) {
@@ -1861,7 +1861,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = ActivityTaskManager.getService()
-                .startActivity(whoThread, who.getBasePackageName(), intent,
+                .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target, requestCode, 0, null, options);
             checkStartActivityResult(result, intent);
@@ -1928,8 +1928,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = ActivityTaskManager.getService()
-                .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
-                        intent.resolveTypeIfNeeded(who.getContentResolver()),
+                .startActivityAsUser(whoThread, who.getBasePackageName(), who.getFeatureId(),
+                        intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, resultWho,
                         requestCode, 0, null, options, user.getIdentifier());
             checkStartActivityResult(result, intent);
@@ -2022,7 +2022,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
-                    intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options);
+                    who.getFeatureId(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), options);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 909a476..f26e628 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -346,6 +346,26 @@
     }
 
     /**
+     * Removes an individual historical notification and regenerates the string pool
+     */
+    public boolean removeNotificationFromWrite(String packageName, long postedTime) {
+        boolean removed = false;
+        for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
+            HistoricalNotification hn = mNotificationsToWrite.get(i);
+            if (packageName.equals(hn.getPackage())
+                    && postedTime == hn.getPostedTimeMs()) {
+                removed = true;
+                mNotificationsToWrite.remove(i);
+            }
+        }
+        if (removed) {
+            poolStringsFromNotifications();
+        }
+
+        return removed;
+    }
+
+    /**
      * Gets pooled strings in order to write them to disk
      */
     public @NonNull String[] getPooledStringsToWrite() {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index b8348c7..f68c929 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -354,8 +354,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, context.getUserId());
@@ -380,8 +380,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, user.getIdentifier());
@@ -497,8 +497,8 @@
         }
         try {
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, intents, resolvedTypes, flags, options,
                     context.getUserId());
             return target != null ? new PendingIntent(target) : null;
@@ -523,8 +523,8 @@
         }
         try {
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, intents, resolvedTypes,
                     flags, options, user.getIdentifier());
             return target != null ? new PendingIntent(target) : null;
@@ -575,8 +575,8 @@
         try {
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_BROADCAST, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_BROADCAST, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, userHandle.getIdentifier());
@@ -654,8 +654,8 @@
         try {
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    serviceKind, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    serviceKind, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, context.getUserId());
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f32a4ab..0e0161f 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -700,6 +700,27 @@
     /** @hide */
     public static final String REMOTE_CALLBACK_RESULT = "result";
 
+    /**
+     * How long we wait for an attached process to publish its content providers
+     * before we decide it must be hung.
+     * @hide
+     */
+    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
+
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
+     * @hide
+     */
+    public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
+
+    // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
+    // long ActivityManagerService is giving a content provider to get published if a new process
+    // needs to be started for that.
+    private static final int GET_TYPE_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
+
     public ContentResolver(@Nullable Context context) {
         this(context, null);
     }
@@ -849,8 +870,6 @@
         }
     }
 
-    private static final int GET_TYPE_TIMEOUT_MILLIS = 3000;
-
     private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
         @GuardedBy("this")
         public boolean done;
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index edc20d9..2ba2840 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -87,6 +87,7 @@
             mService.startActivityAsUser(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
+                    mContext.getFeatureId(),
                     component,
                     targetUser.getIdentifier(),
                     true);
@@ -114,6 +115,7 @@
             mService.startActivityAsUserByIntent(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
+                    mContext.getFeatureId(),
                     intent,
                     targetUser.getIdentifier());
         } catch (RemoteException ex) {
@@ -139,7 +141,8 @@
     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), component, targetUser.getIdentifier(), false);
+                    mContext.getPackageName(), mContext.getFeatureId(), component,
+                    targetUser.getIdentifier(), false);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index a69b988..98bf2dd 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -28,13 +28,14 @@
  */
 interface ICrossProfileApps {
     void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
-            in ComponentName component, int userId, boolean launchMainActivity);
+            in String callingFeatureId, in ComponentName component, int userId,
+            boolean launchMainActivity);
     void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
-                        in Intent intent, int userId);
+            in String callingFeatureId, in Intent intent, int userId);
     List<UserHandle> getTargetUserProfiles(in String callingPackage);
     boolean canInteractAcrossProfiles(in String callingPackage);
     boolean canRequestInteractAcrossProfiles(in String callingPackage);
     void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
     boolean canConfigureInteractAcrossProfiles(in String packageName);
     void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 0492359..b5f4f80 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -49,13 +49,13 @@
     ActivityInfo resolveActivity(
             String callingPackage, in ComponentName component, in UserHandle user);
     void startSessionDetailsActivityAsUser(in IApplicationThread caller, String callingPackage,
-                in PackageInstaller.SessionInfo sessionInfo, in Rect sourceBounds, in Bundle opts,
-                in UserHandle user);
+                String callingFeatureId, in PackageInstaller.SessionInfo sessionInfo,
+                in Rect sourceBounds, in Bundle opts, in UserHandle user);
     void startActivityAsUser(in IApplicationThread caller, String callingPackage,
-            in ComponentName component, in Rect sourceBounds,
+            String callingFeatureId, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
-    void showAppDetailsAsUser(in IApplicationThread caller,
-            String callingPackage, in ComponentName component, in Rect sourceBounds,
+    void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
+            String callingFeatureId, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
     boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user);
     Bundle getSuspendedPackageLauncherExtras(String packageName, in UserHandle user);
@@ -72,7 +72,7 @@
             int flags, in UserHandle user);
     void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
             in UserHandle user);
-    boolean startShortcut(String callingPackage, String packageName, String id,
+    boolean startShortcut(String callingPackage, String packageName, String featureId, String id,
             in Rect sourceBounds, in Bundle startActivityOptions, int userId);
 
     int getShortcutIconResId(String callingPackage, String packageName, String id,
@@ -98,4 +98,9 @@
             in ComponentName componentName, int flags, in IShortcutChangeCallback callback,
             int callbackId);
     void unregisterShortcutChangeCallback(String callingPackage, int callbackId);
+
+    void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
+            in UserHandle user);
+    void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
+            in UserHandle user);
 }
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index f692db1..84f5021 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -35,6 +35,8 @@
     public final String resolvedType;
     /** The name of the package requesting the instant application */
     public final String callingPackage;
+    /** The feature in the package requesting the instant application */
+    public final String callingFeatureId;
     /** Whether or not the requesting package was an instant app */
     public final boolean isRequesterInstantApp;
     /** ID of the user requesting the instant application */
@@ -57,13 +59,15 @@
     public final String token;
 
     public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
-            String resolvedType, String callingPackage, boolean isRequesterInstantApp,
-            int userId, Bundle verificationBundle, boolean resolveForStart,
-            @Nullable int[] hostDigestPrefixSecure, @NonNull String token) {
+            String resolvedType, String callingPackage, @Nullable String callingFeatureId,
+            boolean isRequesterInstantApp, int userId, Bundle verificationBundle,
+            boolean resolveForStart, @Nullable int[] hostDigestPrefixSecure,
+            @NonNull String token) {
         this.responseObj = responseObj;
         this.origIntent = origIntent;
         this.resolvedType = resolvedType;
         this.callingPackage = callingPackage;
+        this.callingFeatureId = callingFeatureId;
         this.isRequesterInstantApp = isRequesterInstantApp;
         this.userId = userId;
         this.verificationBundle = verificationBundle;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 73c9e4d..271d5e4 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -20,6 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -714,7 +715,7 @@
         }
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(),
+                    mContext.getPackageName(), mContext.getFeatureId(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -732,8 +733,8 @@
             @Nullable Rect sourceBounds, @Nullable Bundle opts) {
         try {
             mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), sessionInfo, sourceBounds, opts,
-                    sessionInfo.getUser());
+                    mContext.getPackageName(), mContext.getFeatureId(), sessionInfo, sourceBounds,
+                    opts, sessionInfo.getUser());
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -753,7 +754,7 @@
         logErrorForInvalidProfileAccess(user);
         try {
             mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(),
+                    mContext.getPackageName(), mContext.getFeatureId(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -1089,6 +1090,61 @@
     }
 
     /**
+     * Mark shortcuts as cached for a package.
+     *
+     * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts
+     * in the list will be ignored.
+     *
+     * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned
+     * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same
+     * shortcut, it can be uncached by any valid caller.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be cached.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
+    public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.cacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove cached flag from shortcuts for a package.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be uncached.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
+    public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.uncacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide kept for testing.
      */
     @Deprecated
@@ -1290,9 +1346,9 @@
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
             int userId) {
         try {
-            final boolean success =
-                    mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
-                    sourceBounds, startActivityOptions, userId);
+            final boolean success = mService.startShortcut(mContext.getPackageName(), packageName,
+                    null /* default featureId */, shortcutId, sourceBounds, startActivityOptions,
+                    userId);
             if (!success) {
                 throw new ActivityNotFoundException("Shortcut could not be started");
             }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9f7f482..e8668f1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -597,6 +597,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int MODULE_APEX_NAME = 0x00000001;
 
     /** @hide */
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a11a1dd..a69905e 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -85,4 +85,11 @@
 
     public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage,
             int callingUid);
+
+    public abstract void cacheShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
+    public abstract void uncacheShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 9bd3992..c1df5b6 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -221,10 +221,8 @@
     /**
      * Set a model specific {@link ModelParams} with the given value. This
      * parameter will keep its value for the duration the model is loaded regardless of starting
-     * and
-     * stopping recognition. Once the model is unloaded, the value will be lost.
-     * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before calling
-     * this method.
+     * and stopping recognition. Once the model is unloaded, the value will be lost.
+     * {@link #queryParameter} should be checked first before calling this method.
      *
      * @param soundModelHandle handle of model to apply parameter
      * @param modelParam       {@link ModelParams}
@@ -251,22 +249,14 @@
      * for the duration the model is loaded regardless of starting and stopping recognition.
      * Once the model is unloaded, the value will be lost. If the value is not set, a default
      * value is returned. See {@link ModelParams} for parameter default values.
-     * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before
+     * {@link #queryParameter} should be checked first before
      * calling this method. Otherwise, an exception can be thrown.
      *
      * @param soundModelHandle handle of model to get parameter
      * @param modelParam       {@link ModelParams}
      * @return value of parameter
-     * @throws UnsupportedOperationException if hal or model do not support this API.
-     *                                       {@link SoundTriggerModule#queryParameter(int, int)}
-     *                                       should
-     *                                       be checked first.
-     * @throws IllegalArgumentException      if invalid model handle or parameter is passed.
-     *                                       {@link SoundTriggerModule#queryParameter(int, int)}
-     *                                       should be checked first.
      */
-    public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam)
-            throws UnsupportedOperationException, IllegalArgumentException {
+    public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam) {
         try {
             return mService.getModelParameter(soundModelHandle,
                     ConversionUtil.api2aidlModelParameter(modelParam));
@@ -276,9 +266,8 @@
     }
 
     /**
-     * Determine if parameter control is supported for the given model handle.
-     * This method should be checked prior to calling {@link SoundTriggerModule#setParameter} or
-     * {@link SoundTriggerModule#getParameter}.
+     * Query the parameter support and range for a given {@link ModelParams}.
+     * This method should be check prior to calling {@link #setParameter} or {@link #getParameter}.
      *
      * @param soundModelHandle handle of model to get parameter
      * @param modelParam       {@link ModelParams}
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 26d9c7d..7512352 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -27,6 +27,7 @@
 
 import java.io.Closeable;
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.DirectByteBuffer;
 import java.nio.NioUtils;
@@ -272,6 +273,20 @@
         dest.writeFileDescriptor(mFileDescriptor);
     }
 
+    /**
+     * Returns a dup'd ParcelFileDescriptor from the SharedMemory FileDescriptor.
+     * This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     * TODO: propose this method as a public or system API for next release to achieve parity with
+     *  NDK ASharedMemory_dupFromJava.
+     *
+     * @hide
+     */
+    public ParcelFileDescriptor getFdDup() throws IOException {
+        return ParcelFileDescriptor.dup(mFileDescriptor);
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR =
             new Parcelable.Creator<SharedMemory>() {
         @Override
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 21434a2..9d98b3b 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -103,4 +103,9 @@
      * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
      */
     void deleteStorage(int storageId);
+
+    /**
+     * Setting up native library directories and extract native libs onto a storage.
+     */
+    boolean configureNativeBinaries(int storageId, in @utf8InCpp String apkFullPath, in @utf8InCpp String libDirRelativePath, in @utf8InCpp String abi);
 }
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 0024ac7..ba38268 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -229,16 +229,41 @@
         if (linkedApkStorage == null) {
             throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent);
         }
-        linkedApkStorage.makeDirectory(afterCodePathName);
-        File[] files = beforeCodeFile.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            if (files[i].isFile()) {
-                String fileName = files[i].getName();
-                apkStorage.makeLink(
-                        fileName, linkedApkStorage, afterCodePathName + "/" + fileName);
+        linkFiles(apkStorage, beforeCodeFile, "", linkedApkStorage, afterCodePathName);
+        apkStorage.unBind(beforeCodePath);
+    }
+
+    /**
+     * Recursively set up directories and link all the files from source storage to target storage.
+     *
+     * @param sourceStorage The storage that has all the files and directories underneath.
+     * @param sourceAbsolutePath The absolute path of the directory that holds all files and dirs.
+     * @param sourceRelativePath The relative path on the source directory, e.g., "" or "lib".
+     * @param targetStorage The target storage that will have the same files and directories.
+     * @param targetRelativePath The relative path to the directory on the target storage that
+     *                           should have all the files and dirs underneath,
+     *                           e.g., "packageName-random".
+     * @throws IOException When makeDirectory or makeLink fails on the Incremental File System.
+     */
+    private void linkFiles(IncrementalStorage sourceStorage, File sourceAbsolutePath,
+            String sourceRelativePath, IncrementalStorage targetStorage,
+            String targetRelativePath) throws IOException {
+        targetStorage.makeDirectory(targetRelativePath);
+        final File[] entryList = sourceAbsolutePath.listFiles();
+        for (int i = 0; i < entryList.length; i++) {
+            final File entry = entryList[i];
+            final String entryName = entryList[i].getName();
+            final String sourceEntryRelativePath =
+                    sourceRelativePath.isEmpty() ? entryName : sourceRelativePath + "/" + entryName;
+            final String targetEntryRelativePath = targetRelativePath + "/" + entryName;
+            if (entry.isFile()) {
+                sourceStorage.makeLink(
+                        sourceEntryRelativePath, targetStorage, targetEntryRelativePath);
+            } else if (entry.isDirectory()) {
+                linkFiles(sourceStorage, entry, sourceEntryRelativePath, targetStorage,
+                        targetEntryRelativePath);
             }
         }
-        apkStorage.unBind(beforeCodePath);
     }
 
     /**
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index c4b843b..5df44ff 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -416,4 +416,24 @@
             return false;
         }
     }
+
+    /**
+     * Configure all the lib files inside Incremental Service, e.g., create lib dirs, create new lib
+     * files, extract original lib file data from zip and then write data to the lib files on the
+     * Incremental File System.
+     *
+     * @param apkFullPath Source APK to extract native libs from.
+     * @param libDirRelativePath Target dir to put lib files, e.g., "lib" or "lib/arm".
+     * @param abi Target ABI of the native lib files. Only extract native libs of this ABI.
+     * @return Success of not.
+     */
+    public boolean configureNativeBinaries(String apkFullPath, String libDirRelativePath,
+            String abi) {
+        try {
+            return mService.configureNativeBinaries(mId, apkFullPath, libDirRelativePath, abi);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 30e4eee08..b9207e5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1830,6 +1830,17 @@
     public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
 
     /**
+     * Activity Extra: The {@link NotificationChannel#getConversationId()} of the notification
+     * conversation settings to display.
+     * <p>
+     * This is an optional extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}. If
+     * included the system will first look up notification settings by channel and conversation id,
+     * and will fall back to channel id if a specialized channel for this conversation doesn't
+     * exist, similar to {@link NotificationManager#getNotificationChannel(String, String)}.
+     */
+    public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
+
+    /**
      * Activity Action: Show notification redaction settings.
      *
      * @hide
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/core/java/android/service/notification/ConversationChannelWrapper.aidl
similarity index 76%
rename from core/java/android/os/StatsDimensionsValue.aidl
rename to core/java/android/service/notification/ConversationChannelWrapper.aidl
index 81a14a4..f324158 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/core/java/android/service/notification/ConversationChannelWrapper.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.service.notification;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+parcelable ConversationChannelWrapper;
\ No newline at end of file
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
new file mode 100644
index 0000000..9847695
--- /dev/null
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.app.NotificationChannel;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class ConversationChannelWrapper implements Parcelable {
+
+    private NotificationChannel mNotificationChannel;
+    private CharSequence mGroupLabel;
+    private CharSequence mParentChannelLabel;
+    private ShortcutInfo mShortcutInfo;
+
+    public ConversationChannelWrapper() {}
+
+    protected ConversationChannelWrapper(Parcel in) {
+        mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader());
+        mGroupLabel = in.readCharSequence();
+        mParentChannelLabel = in.readCharSequence();
+        mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mNotificationChannel, flags);
+        dest.writeCharSequence(mGroupLabel);
+        dest.writeCharSequence(mParentChannelLabel);
+        dest.writeParcelable(mShortcutInfo, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ConversationChannelWrapper> CREATOR =
+            new Creator<ConversationChannelWrapper>() {
+                @Override
+                public ConversationChannelWrapper createFromParcel(Parcel in) {
+                    return new ConversationChannelWrapper(in);
+                }
+
+                @Override
+                public ConversationChannelWrapper[] newArray(int size) {
+                    return new ConversationChannelWrapper[size];
+                }
+            };
+
+
+    public NotificationChannel getNotificationChannel() {
+        return mNotificationChannel;
+    }
+
+    public void setNotificationChannel(
+            NotificationChannel notificationChannel) {
+        mNotificationChannel = notificationChannel;
+    }
+
+    public CharSequence getGroupLabel() {
+        return mGroupLabel;
+    }
+
+    public void setGroupLabel(CharSequence groupLabel) {
+        mGroupLabel = groupLabel;
+    }
+
+    public CharSequence getParentChannelLabel() {
+        return mParentChannelLabel;
+    }
+
+    public void setParentChannelLabel(CharSequence parentChannelLabel) {
+        mParentChannelLabel = parentChannelLabel;
+    }
+
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    public void setShortcutInfo(ShortcutInfo shortcutInfo) {
+        mShortcutInfo = shortcutInfo;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ConversationChannelWrapper that = (ConversationChannelWrapper) o;
+        return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
+                Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
+                Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
+                Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
+                getShortcutInfo());
+    }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 36f2c62..8e6f77b 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1314,7 +1314,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startVoiceActivity(mToken, intent,
-                    intent.resolveType(mContext.getContentResolver()));
+                    intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
@@ -1342,7 +1342,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startAssistantActivity(mToken, intent,
-                    intent.resolveType(mContext.getContentResolver()));
+                    intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 531ade0..e65bd9f 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -412,23 +412,23 @@
      * domain. This indication does not necessarily indicate a change of service state, which should
      * be tracked via {@link #LISTEN_SERVICE_STATE}.
      *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onRegistrationFailed()
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
 
     /**
      * Listen for Barring Information for the current registered / camped cell.
      *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onBarringInfoChanged()
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
     /*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a407bd8..a6f8fad 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12729,12 +12729,14 @@
                 return findViewInsideOutShouldExist(root, mNextFocusForwardId);
             case FOCUS_BACKWARD: {
                 if (mID == View.NO_ID) return null;
-                return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
-                    @Override
-                    public boolean test(View t) {
-                        return t.findViewById(t.mNextFocusForwardId) == View.this;
-                    }
-                });
+                final View rootView = root;
+                final View startView = this;
+                // Since we have forward links but no backward links, we need to find the view that
+                // forward links to this view. We can't just find the view with the specified ID
+                // because view IDs need not be unique throughout the tree.
+                return root.findViewByPredicateInsideOut(startView,
+                    t -> findViewInsideOutShouldExist(rootView, t, t.mNextFocusForwardId)
+                            == startView);
             }
         }
         return null;
@@ -12764,11 +12766,15 @@
     }
 
     private View findViewInsideOutShouldExist(View root, int id) {
+        return findViewInsideOutShouldExist(root, this, id);
+    }
+
+    private View findViewInsideOutShouldExist(View root, View start, int id) {
         if (mMatchIdPredicate == null) {
             mMatchIdPredicate = new MatchIdPredicate();
         }
         mMatchIdPredicate.mId = id;
-        View result = root.findViewByPredicateInsideOut(this, mMatchIdPredicate);
+        View result = root.findViewByPredicateInsideOut(start, mMatchIdPredicate);
         if (result == null) {
             Log.w(VIEW_LOG_TAG, "couldn't find view with id " + id);
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2c30d29..159b93e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -222,6 +222,13 @@
      * @see #USE_NEW_INSETS_PROPERTY
      * @hide
      */
+    public static int sNewInsetsMode =
+            SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, 0);
+
+    /**
+     * @see #USE_NEW_INSETS_PROPERTY
+     * @hide
+     */
     public static final int NEW_INSETS_MODE_NONE = 0;
 
     /**
@@ -237,13 +244,6 @@
     public static final int NEW_INSETS_MODE_FULL = 2;
 
     /**
-     * @see #USE_NEW_INSETS_PROPERTY
-     * @hide
-     */
-    public static int sNewInsetsMode =
-            SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, NEW_INSETS_MODE_IME);
-
-    /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
      */
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f0c16aa..dbab81b1 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -664,8 +664,6 @@
          */
         @Override
         public void setCurrentRootView(ViewRootImpl rootView) {
-            // If the mCurRootView is losing window focus, release the strong reference to it
-            // so as not to prevent it from being garbage-collected.
             if (mWindowFocusGainFuture != null) {
                 mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
                 mWindowFocusGainFuture = null;
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index a2c70b9..8c52d1f 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -263,9 +263,14 @@
     /**
      * Return the view.
      *
-     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
-     * targeting API level {@link Build.VERSION_CODES#R} or higher that haven't called {@link
-     * #setView(View)} with a non-{@code null} view, this method will return {@code null}.
+     * <p>Toasts constructed with {@link #Toast(Context)} that haven't called {@link #setView(View)}
+     * with a non-{@code null} view will return {@code null} here.
+     *
+     * <p>Starting from Android {@link Build.VERSION_CODES#R}, in apps targeting API level {@link
+     * Build.VERSION_CODES#R} or higher, toasts constructed with {@link #makeText(Context,
+     * CharSequence, int)} or its variants will also return {@code null} here unless they had called
+     * {@link #setView(View)} with a non-{@code null} view. If you want to be notified when the
+     * toast is shown or hidden, use {@link #addCallback(Callback)}.
      *
      * @see #setView
      * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
@@ -276,7 +281,7 @@
      *      will not have custom toast views displayed.
      */
     @Deprecated
-    public View getView() {
+    @Nullable public View getView() {
         return mNextView;
     }
 
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index f3b6d29..417e23f 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -38,8 +38,10 @@
             IVoiceInteractor interactor);
     boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
     boolean hideSessionFromSession(IBinder token);
-    int startVoiceActivity(IBinder token, in Intent intent, String resolvedType);
-    int startAssistantActivity(IBinder token, in Intent intent, String resolvedType);
+    int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
+            String callingFeatureId);
+    int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
+            String callingFeatureId);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index a0de51d..0ccc45e 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -25,6 +25,7 @@
 import static android.system.OsConstants.S_IXGRP;
 import static android.system.OsConstants.S_IXOTH;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -32,7 +33,12 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Slog;
@@ -44,6 +50,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.List;
 
 /**
@@ -481,8 +488,54 @@
      */
     private static int incrementalConfigureNativeBinariesForSupportedAbi(Handle handle,
             File libSubDir, String abi) {
-        // TODO(b/136132412): implement this
-        return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        final String[] apkPaths = handle.apkPaths;
+        if (apkPaths == null || apkPaths.length == 0) {
+            Slog.e(TAG, "No apks to extract native libraries from.");
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        }
+
+        final IBinder incrementalService = ServiceManager.getService(Context.INCREMENTAL_SERVICE);
+        if (incrementalService == null) {
+            //TODO(b/133435829): add incremental specific error codes
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        }
+        final IncrementalManager incrementalManager = new IncrementalManager(
+                IIncrementalService.Stub.asInterface(incrementalService));
+        final File apkParent = new File(apkPaths[0]).getParentFile();
+        IncrementalStorage incrementalStorage =
+                incrementalManager.openStorage(apkParent.getAbsolutePath());
+        if (incrementalStorage == null) {
+            Slog.e(TAG, "Failed to find incremental storage");
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        }
+
+        String libRelativeDir = getRelativePath(apkParent, libSubDir);
+        if (libRelativeDir == null) {
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        }
+
+        for (int i = 0; i < apkPaths.length; i++) {
+            if (!incrementalStorage.configureNativeBinaries(apkPaths[i], libRelativeDir, abi)) {
+                return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            }
+        }
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
+    private static String getRelativePath(File base, File target) {
+        try {
+            final Path basePath = base.toPath();
+            final Path targetPath = target.toPath();
+            final Path relativePath = basePath.relativize(targetPath);
+            if (relativePath.toString().isEmpty()) {
+                return "";
+            }
+            return relativePath.toString();
+        } catch (IllegalArgumentException ex) {
+            Slog.e(TAG, "Failed to find relative path between: " + base.getAbsolutePath()
+                    + " and: " + target.getAbsolutePath());
+            return null;
+        }
     }
 
     // We don't care about the other return values for now.
diff --git a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
new file mode 100644
index 0000000..1fce098
--- /dev/null
+++ b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Given a move coordinate (x, y), the original taks bounds and relevant details, calculate the new
+ * bounds.
+ *
+ * @hide
+ */
+public class TaskResizingAlgorithm {
+
+    @IntDef(flag = true,
+            value = {
+                    CTRL_NONE,
+                    CTRL_LEFT,
+                    CTRL_RIGHT,
+                    CTRL_TOP,
+                    CTRL_BOTTOM
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CtrlType {}
+
+    public static final int CTRL_NONE   = 0x0;
+    public static final int CTRL_LEFT   = 0x1;
+    public static final int CTRL_RIGHT  = 0x2;
+    public static final int CTRL_TOP    = 0x4;
+    public static final int CTRL_BOTTOM = 0x8;
+
+    // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
+    // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
+    // aspect he desires.
+    @VisibleForTesting
+    public static final float MIN_ASPECT = 1.2f;
+
+    /**
+     * Given a (x, y) point and its original starting down point and its original bounds, calculate
+     * and return a new resized bound.
+     * @param x the new moved X point.
+     * @param y the new moved Y point.
+     * @param startDragX the original starting X point.
+     * @param startDragY the original starting Y point.
+     * @param originalBounds the original bound before resize.
+     * @param ctrlType The type of resize operation.
+     * @param minVisibleWidth The minimal width required for the new size.
+     * @param minVisibleHeight The minimal height required for the new size.
+     * @param maxVisibleSize The maximum size allowed.
+     * @param preserveOrientation
+     * @param startOrientationWasLandscape
+     * @return
+     */
+    public static Rect resizeDrag(float x, float y, float startDragX, float startDragY,
+            Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight,
+            Point maxVisibleSize, boolean preserveOrientation,
+            boolean startOrientationWasLandscape) {
+        // This is a resizing operation.
+        // We need to keep various constraints:
+        // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
+        // 2. The orientation is kept - if required.
+        final int deltaX = Math.round(x - startDragX);
+        final int deltaY = Math.round(y - startDragY);
+        int left = originalBounds.left;
+        int top = originalBounds.top;
+        int right = originalBounds.right;
+        int bottom = originalBounds.bottom;
+
+        // Calculate the resulting width and height of the drag operation.
+        int width = right - left;
+        int height = bottom - top;
+        if ((ctrlType & CTRL_LEFT) != 0) {
+            width = Math.max(minVisibleWidth, width - deltaX);
+        } else if ((ctrlType & CTRL_RIGHT) != 0) {
+            width = Math.max(minVisibleWidth, width + deltaX);
+        }
+        if ((ctrlType & CTRL_TOP) != 0) {
+            height = Math.max(minVisibleHeight, height - deltaY);
+        } else if ((ctrlType & CTRL_BOTTOM) != 0) {
+            height = Math.max(minVisibleHeight, height + deltaY);
+        }
+
+        // If we have to preserve the orientation - check that we are doing so.
+        final float aspect = (float) width / (float) height;
+        if (preserveOrientation && ((startOrientationWasLandscape && aspect < MIN_ASPECT)
+                || (!startOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
+            // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
+            // drag axis. What ever is producing the bigger rectangle will be chosen.
+            int width1;
+            int width2;
+            int height1;
+            int height2;
+            if (startOrientationWasLandscape) {
+                // Assuming that the width is our target we calculate the height.
+                width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
+                height1 = Math.min(height, Math.round((float) width1 / MIN_ASPECT));
+                if (height1 < minVisibleHeight) {
+                    // If the resulting height is too small we adjust to the minimal size.
+                    height1 = minVisibleHeight;
+                    width1 = Math.max(minVisibleWidth,
+                            Math.min(maxVisibleSize.x, Math.round((float) height1 * MIN_ASPECT)));
+                }
+                // Assuming that the height is our target we calculate the width.
+                height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
+                width2 = Math.max(width, Math.round((float) height2 * MIN_ASPECT));
+                if (width2 < minVisibleWidth) {
+                    // If the resulting width is too small we adjust to the minimal size.
+                    width2 = minVisibleWidth;
+                    height2 = Math.max(minVisibleHeight,
+                            Math.min(maxVisibleSize.y, Math.round((float) width2 / MIN_ASPECT)));
+                }
+            } else {
+                // Assuming that the width is our target we calculate the height.
+                width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
+                height1 = Math.max(height, Math.round((float) width1 * MIN_ASPECT));
+                if (height1 < minVisibleHeight) {
+                    // If the resulting height is too small we adjust to the minimal size.
+                    height1 = minVisibleHeight;
+                    width1 = Math.max(minVisibleWidth,
+                            Math.min(maxVisibleSize.x, Math.round((float) height1 / MIN_ASPECT)));
+                }
+                // Assuming that the height is our target we calculate the width.
+                height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
+                width2 = Math.min(width, Math.round((float) height2 / MIN_ASPECT));
+                if (width2 < minVisibleWidth) {
+                    // If the resulting width is too small we adjust to the minimal size.
+                    width2 = minVisibleWidth;
+                    height2 = Math.max(minVisibleHeight,
+                            Math.min(maxVisibleSize.y, Math.round((float) width2 * MIN_ASPECT)));
+                }
+            }
+
+            // Use the bigger of the two rectangles if the major change was positive, otherwise
+            // do the opposite.
+            final boolean grows = width > (right - left) || height > (bottom - top);
+            if (grows == (width1 * height1 > width2 * height2)) {
+                width = width1;
+                height = height1;
+            } else {
+                width = width2;
+                height = height2;
+            }
+        }
+
+        // Generate the final bounds by keeping the opposite drag edge constant.
+        if ((ctrlType & CTRL_LEFT) != 0) {
+            left = right - width;
+        } else { // Note: The right might have changed - if we pulled at the right or not.
+            right = left + width;
+        }
+        if ((ctrlType & CTRL_TOP) != 0) {
+            top = bottom - height;
+        } else { // Note: The height might have changed - if we pulled at the bottom or not.
+            bottom = top + height;
+        }
+        return new Rect(left, top, right, bottom);
+    }
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 26d2f19..2fcc1de 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -903,7 +903,6 @@
                     } break;
                     case "component-override": {
                         readComponentOverrides(parser, permFile);
-                        XmlUtils.skipCurrentTag(parser);
                     } break;
                     case "backup-transport-whitelisted-service": {
                         if (allowFeatures) {
@@ -1403,8 +1402,7 @@
 
         final int depth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, depth)) {
-            String name = parser.getName();
-            if ("component".equals(name)) {
+            if ("component".equals(parser.getName())) {
                 String clsname = parser.getAttributeValue(null, "class");
                 String enabled = parser.getAttributeValue(null, "enabled");
                 if (clsname == null) {
@@ -1432,8 +1430,6 @@
                 }
 
                 componentEnabledStates.put(clsname, !"false".equals(enabled));
-            } else {
-                XmlUtils.skipCurrentTag(parser);
             }
         }
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3d0926d..76e7e19 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -182,6 +182,7 @@
                 "android_hardware_UsbRequest.cpp",
                 "android_hardware_location_ActivityRecognitionHardware.cpp",
                 "android_util_FileObserver.cpp",
+                "android/graphics/GraphicsStatsService.cpp",
                 "android/graphics/SurfaceTexture.cpp",
                 "android/opengl/poly_clip.cpp", // TODO: .arm
                 "android/opengl/util.cpp",
@@ -273,6 +274,7 @@
                 "libstats_jni",
                 "libstatslog",
                 "server_configurable_flags",
+                "libstatspull",
             ],
             export_shared_lib_headers: [
                 // AndroidRuntime.h depends on nativehelper/jni.h
diff --git a/core/jni/android/graphics/GraphicsStatsService.cpp b/core/jni/android/graphics/GraphicsStatsService.cpp
new file mode 100644
index 0000000..ef0aacc
--- /dev/null
+++ b/core/jni/android/graphics/GraphicsStatsService.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicsStatsService"
+
+#include <JankTracker.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <service/GraphicsStatsService.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace android::uirenderer;
+
+static jint getAshmemSize(JNIEnv*, jobject) {
+    return sizeof(ProfileData);
+}
+
+static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
+    GraphicsStatsService::Dump* dump =
+            GraphicsStatsService::createDump(fd,
+                                             isProto ? GraphicsStatsService::DumpType::Protobuf
+                                                     : GraphicsStatsService::DumpType::Text);
+    return reinterpret_cast<jlong>(dump);
+}
+
+static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
+                      jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    std::string path;
+    const ProfileData* data = nullptr;
+    LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
+    ScopedByteArrayRO buffer{env};
+    if (jdata != nullptr) {
+        buffer.reset(jdata);
+        LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                            "Buffer size %zu doesn't match expected %zu!", buffer.size(),
+                            sizeof(ProfileData));
+        data = reinterpret_cast<const ProfileData*>(buffer.get());
+    }
+    if (jpath != nullptr) {
+        ScopedUtfChars pathChars(env, jpath);
+        LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(),
+                            "Failed to get path chars");
+        path.assign(pathChars.c_str(), pathChars.size());
+    }
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
+                        "Failed to get path chars");
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
+
+    const std::string package(packageChars.c_str(), packageChars.size());
+    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
+}
+
+static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    const std::string path(pathChars.c_str(), pathChars.size());
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::addToDump(dump, path);
+}
+
+static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::finishDump(dump);
+}
+
+static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData,
+                               jboolean lastFullDay) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    AStatsEventList* data = reinterpret_cast<AStatsEventList*>(pulledData);
+    GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE);
+}
+
+static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
+                       jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    ScopedByteArrayRO buffer(env, jdata);
+    LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                        "Buffer size %zu doesn't match expected %zu!", buffer.size(),
+                        sizeof(ProfileData));
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
+                        "Failed to get path chars");
+
+    const std::string path(pathChars.c_str(), pathChars.size());
+    const std::string package(packageChars.c_str(), packageChars.size());
+    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
+    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
+}
+
+static jobject gGraphicsStatsServiceObject = nullptr;
+static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
+
+static JNIEnv* getJNIEnv() {
+    JavaVM* vm = AndroidRuntime::getJavaVM();
+    JNIEnv* env = nullptr;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+        }
+    }
+    return env;
+}
+
+// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
+static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
+                                                                      AStatsEventList* data,
+                                                                      void* cookie) {
+    JNIEnv* env = getJNIEnv();
+    if (!env) {
+        return false;
+    }
+    if (gGraphicsStatsServiceObject == nullptr) {
+        ALOGE("Failed to get graphicsstats service");
+        return AStatsManager_PULL_SKIP;
+    }
+
+    for (bool lastFullDay : {true, false}) {
+        env->CallVoidMethod(gGraphicsStatsServiceObject,
+                            gGraphicsStatsService_pullGraphicsStatsMethodID,
+                            (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE),
+                            reinterpret_cast<jlong>(data));
+        if (env->ExceptionCheck()) {
+            env->ExceptionDescribe();
+            env->ExceptionClear();
+            ALOGE("Failed to invoke graphicsstats service");
+            return AStatsManager_PULL_SKIP;
+        }
+    }
+    return AStatsManager_PULL_SUCCESS;
+}
+
+// Register a puller for GRAPHICS_STATS atom with the statsd service.
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+    gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
+    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000);  // 10 milliseconds
+    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
+
+    AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
+                                           &graphicsStatsPullCallback, metadata, nullptr);
+
+    AStatsManager_PullAtomMetadata_release(metadata);
+}
+
+static void nativeDestructor(JNIEnv* env, jobject javaObject) {
+    AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
+    env->DeleteGlobalRef(gGraphicsStatsServiceObject);
+    gGraphicsStatsServiceObject = nullptr;
+}
+
+static const JNINativeMethod sMethods[] =
+        {{"nGetAshmemSize", "()I", (void*)getAshmemSize},
+         {"nCreateDump", "(IZ)J", (void*)createDump},
+         {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump},
+         {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
+         {"nFinishDump", "(J)V", (void*)finishDump},
+         {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
+         {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer},
+         {"nativeInit", "()V", (void*)nativeInit},
+         {"nativeDestructor", "()V", (void*)nativeDestructor}};
+
+int register_android_graphics_GraphicsStatsService(JNIEnv* env) {
+    jclass graphicsStatsService_class =
+            FindClassOrDie(env, "android/graphics/GraphicsStatsService");
+    gGraphicsStatsService_pullGraphicsStatsMethodID =
+            GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V");
+    return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods,
+                                    NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index c14d99c..4625418 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -708,6 +708,12 @@
 
     // ACTION: Deny "Access all files" for an app
     APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY = 1731;
+
+    // ACTION: Battery feature usage
+    ACTION_BATTERY_OPTION_FEATURE_USAGE = 1732;
+
+    // ACTION: Battery feature runtime event
+    ACTION_BATTERY_OPTION_RUNTIME_EVENT = 1733;
 }
 
 /**
@@ -2581,4 +2587,8 @@
 
     // OPEN: Settings -> Apps & Notifications -> Special App Access
     INTERACT_ACROSS_PROFILES = 1829;
+
+    // OPEN: Settings > Notifications > (app or conversations) > conversation
+    NOTIFICATION_CONVERSATION_SETTINGS = 1830;
+
 }
diff --git a/core/proto/android/service/sensor_service.proto b/core/proto/android/service/sensor_service.proto
index 8598f86..48f6670 100644
--- a/core/proto/android/service/sensor_service.proto
+++ b/core/proto/android/service/sensor_service.proto
@@ -39,7 +39,7 @@
         OP_MODE_RESTRICTED = 2;
         OP_MODE_DATA_INJECTION = 3;
     }
-
+    optional sint32 init_status = 16;
     optional int64 current_time_ms = 1;
     optional SensorDeviceProto sensor_device = 2;
     optional SensorListProto sensors = 3;
@@ -56,6 +56,8 @@
     repeated SensorEventConnectionProto active_connections = 13;
     repeated SensorDirectConnectionProto direct_connections = 14;
     repeated SensorRegistrationInfoProto previous_registrations = 15;
+
+    // Next tag: 17
 }
 
 // Proto dump of android::SensorDevice
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 46e8f64..b01eb39 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,31 +1,50 @@
-<!--
-Copyright (C) 2020 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="240dp"
-    android:height="240dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:width="512dp"
+    android:height="512dp"
+    android:viewportWidth="512"
+    android:viewportHeight="512">
     <path
-        android:fillColor="#000"
-        android:pathData="M16 4c-2.2 0-4 1.8-4 4v4H4V8c0-2.2 1.8-4 4-4h8z"/>
+        android:fillColor="#F86734"
+        android:pathData="M416.23 236.62h-10.67c-1.46 0-2.65-1.19-2.65-2.65v-9.85c0-1.47 1.19-2.65 2.65-2.65h23.37c1.47 0 2.66 1.19 2.66 2.65v66.9c0 1.46-1.2 2.65-2.66 2.65H418.9c-1.47 0-2.66-1.19-2.66-2.65v-54.4z"/>
     <path
-        android:fillColor="#000"
-        android:pathData="M8 20c2.2 0 4-1.8 4-4v-4H4v8h4z"/>
+        android:fillColor="#F86734"
+        android:pathData="M455.51 236.62h-10.67c-1.47 0-2.65-1.19-2.65-2.65v-9.85c0-1.47 1.18-2.65 2.65-2.65h23.37c1.47 0 2.66 1.19 2.66 2.65v66.9c0 1.46-1.2 2.65-2.66 2.65h-10.05c-1.46 0-2.65-1.19-2.65-2.65v-54.4z"/>
     <path
-        android:fillColor="#80000000"
-        android:pathData="M16 12c2.2 0 4-1.8 4-4V4h-8v8h4z"/>
+        android:fillColor="#D6F0FF"
+        android:pathData="M364.12 400.25a4.34 4.34 0 1 0 0 8.68a4.34 4.34 0 1 0 0-8.68z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M275.46 433.53a4.84 4.84 0 1 0 0 9.68a4.84 4.84 0 1 0 0-9.68z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M184.52 418.83a5.36 5.36 0 1 0 0 10.72a5.36 5.36 0 1 0 0-10.72z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M110.42 359.19a5.89 5.89 0 1 0 0 11.78a5.89 5.89 0 1 0 0-11.78z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M75.94 270.17a6.43 6.43 0 1 0 0 12.86a6.43 6.43 0 1 0 0-12.86z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M89.48 178.57a6.98 6.98 0 1 0 0 13.96a6.98 6.98 0 1 0 0-13.96z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M147.97 103.54a7.54 7.54 0 1 0 0 15.08a7.54 7.54 0 1 0 0-15.08z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M236.63 66.7a8.1 8.1 0 1 0 0 16.2a8.1 8.1 0 1 0 0-16.2z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M327.09 78.3a8.66 8.66 0 1 0 0 17.32a8.66 8.66 0 1 0 0-17.32z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M401.05 136.97a9.22 9.22 0 1 0 0 18.44a9.22 9.22 0 1 0 0-18.44z"/>
+    <group>
+        <path
+            android:fillColor="#3DDB85"
+            android:pathData="M255.45 129.46a128.11 128.11 0 1 0 0 256.22a128.11 128.11 0 1 0 0-256.22z"/>
+        <path
+            android:fillColor="#FFF"
+            android:pathData="M339.23 236.09a21.48 21.48 0 1 0 0 42.96a21.48 21.48 0 1 0 0-42.96z"/>
+    </group>
 </vector>
-
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 0e9aab2..700781b 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -1,17 +1,17 @@
-<!--
-Copyright (C) 2020 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
+     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
+          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.
+     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.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
@@ -19,12 +19,22 @@
     android:viewportWidth="24"
     android:viewportHeight="24">
     <path
-        android:fillColor="#000"
-        android:pathData="M16 4c-2.2 0-4 1.8-4 4v4H4V8c0-2.2 1.8-4 4-4h8z"/>
-    <path
-        android:fillColor="#000"
-        android:pathData="M8 20c2.2 0 4-1.8 4-4v-4H4v8h4z"/>
-    <path
-        android:fillColor="#80000000"
-        android:pathData="M16 12c2.2 0 4-1.8 4-4V4h-8v8h4z"/>
+        android:fillColor="#FFFFFF"
+        android:pathData="
+M 12,1
+A 11 11 0 0 0 1,12
+A 11 11 0 1 0 12,1
+Z
+
+M 12.5,8
+a 3,3 0 0 1 6,0
+a 3,3 0 0 1 -6,0
+Z
+
+M 5.5,8
+a 3,3 0 0 1 6,0
+l 0,8
+a 3,3 0 0 1 -6,0
+Z
+"/>
 </vector>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 59335a5..718ca46 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1330,6 +1330,12 @@
             android:process=":FakeProvider">
         </provider>
 
+        <provider
+            android:name="android.content.SlowProvider"
+            android:authorities="android.content.SlowProvider"
+            android:process=":SlowProvider">
+        </provider>
+
         <!-- Application components used for os tests -->
 
         <service android:name="android.os.MessengerService"
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 0a21875..d1608d0 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -234,6 +234,40 @@
     }
 
     @Test
+    public void testRemoveNotificationFromWrite() {
+        NotificationHistory history = new NotificationHistory();
+
+        List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
+        List<String> postRemoveExpectedStrings = new ArrayList<>();
+        for (int i = 1; i <= 10; i++) {
+            HistoricalNotification n = getHistoricalNotification("pkg", i);
+
+            if (987654323 != n.getPostedTimeMs()) {
+                postRemoveExpectedStrings.add(n.getPackage());
+                postRemoveExpectedStrings.add(n.getChannelName());
+                postRemoveExpectedStrings.add(n.getChannelId());
+                postRemoveExpectedEntries.add(n);
+            }
+
+            history.addNotificationToWrite(n);
+        }
+
+        history.poolStringsFromNotifications();
+
+        assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
+        // 1 package name and 10 unique channel names and ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
+
+        history.removeNotificationFromWrite("pkg", 987654323);
+
+
+        // 1 package names and 9 * 2 unique channel names and ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(19);
+        assertThat(history.getNotificationsToWrite())
+                .containsExactlyElementsIn(postRemoveExpectedEntries);
+    }
+
+    @Test
     public void testParceling() {
         NotificationHistory history = new NotificationHistory();
 
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 9dcce1e..6dc7392 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -209,4 +209,13 @@
         String type = mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote"));
         assertEquals("fake/remote", type);
     }
+
+
+    @Test
+    public void testGetType_slowProvider() {
+        // This provider is running in a different process and is intentionally slow to start.
+        // We are trying to confirm that it does not cause an ANR
+        String type = mResolver.getType(Uri.parse("content://android.content.SlowProvider"));
+        assertEquals("slow", type);
+    }
 }
diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java
new file mode 100644
index 0000000..aba32e8
--- /dev/null
+++ b/core/tests/coretests/src/android/content/SlowProvider.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * A dummy content provider for tests.  This provider runs in a different process from the test and
+ * is intentionally slow.
+ */
+public class SlowProvider extends ContentProvider {
+
+    private static final int ON_CREATE_LATENCY_MILLIS = 3000;
+
+    @Override
+    public boolean onCreate() {
+        try {
+            Thread.sleep(ON_CREATE_LATENCY_MILLIS);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "slow";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
similarity index 85%
rename from services/core/java/com/android/server/GraphicsStatsService.java
rename to graphics/java/android/graphics/GraphicsStatsService.java
index 5179fa7..8dfd6ee 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package android.graphics;
 
+import android.annotation.SystemApi;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -26,13 +27,14 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.MemoryFile;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.system.ErrnoException;
 import android.util.Log;
 import android.view.IGraphicsStats;
 import android.view.IGraphicsStatsCallback;
@@ -45,6 +47,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -84,8 +87,8 @@
 
     // This isn't static because we need this to happen after registerNativeMethods, however
     // the class is loaded (and thus static ctor happens) before that occurs.
-    private final int ASHMEM_SIZE = nGetAshmemSize();
-    private final byte[] ZERO_DATA = new byte[ASHMEM_SIZE];
+    private final int mAshmemSize = nGetAshmemSize();
+    private final byte[] mZeroData = new byte[mAshmemSize];
 
     private final Context mContext;
     private final AppOpsManager mAppOps;
@@ -97,6 +100,7 @@
     private Handler mWriteOutHandler;
     private boolean mRotateIsScheduled = false;
 
+    @SystemApi
     public GraphicsStatsService(Context context) {
         mContext = context;
         mAppOps = context.getSystemService(AppOpsManager.class);
@@ -108,7 +112,8 @@
             throw new IllegalStateException("Graphics stats directory does not exist: "
                     + mGraphicsStatsDir.getAbsolutePath());
         }
-        HandlerThread bgthread = new HandlerThread("GraphicsStats-disk", Process.THREAD_PRIORITY_BACKGROUND);
+        HandlerThread bgthread = new HandlerThread("GraphicsStats-disk",
+                Process.THREAD_PRIORITY_BACKGROUND);
         bgthread.start();
 
         mWriteOutHandler = new Handler(bgthread.getLooper(), new Handler.Callback() {
@@ -159,7 +164,7 @@
                 active.mCallback.onRotateGraphicsStatsBuffer();
             } catch (RemoteException e) {
                 Log.w(TAG, String.format("Failed to notify '%s' (pid=%d) to rotate buffers",
-                        active.mInfo.packageName, active.mPid), e);
+                        active.mInfo.mPackageName, active.mPid), e);
             }
         }
         // Give a few seconds for everyone to rotate before doing the cleanup
@@ -167,8 +172,8 @@
     }
 
     @Override
-    public ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback token)
-            throws RemoteException {
+    public ParcelFileDescriptor requestBufferForProcess(String packageName,
+            IGraphicsStatsCallback token) throws RemoteException {
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         ParcelFileDescriptor pfd = null;
@@ -196,7 +201,7 @@
     // current day.
     // This method is invoked from native code only.
     @SuppressWarnings({"UnusedDeclaration"})
-    private long pullGraphicsStats(boolean lastFullDay) throws RemoteException {
+    private void pullGraphicsStats(boolean lastFullDay, long pulledData) throws RemoteException {
         int uid = Binder.getCallingUid();
 
         // DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
@@ -213,13 +218,13 @@
 
         long callingIdentity = Binder.clearCallingIdentity();
         try {
-            return pullGraphicsStatsImpl(lastFullDay);
+            pullGraphicsStatsImpl(lastFullDay, pulledData);
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
         }
     }
 
-    private long pullGraphicsStatsImpl(boolean lastFullDay) {
+    private void pullGraphicsStatsImpl(boolean lastFullDay, long pulledData) {
         long targetDay;
         if (lastFullDay) {
             // Get stats from yesterday. Stats stay constant, because the day is over.
@@ -235,7 +240,7 @@
             buffers = new ArrayList<>(mActive.size());
             for (int i = 0; i < mActive.size(); i++) {
                 ActiveBuffer buffer = mActive.get(i);
-                if (buffer.mInfo.startTime == targetDay) {
+                if (buffer.mInfo.mStartTime == targetDay) {
                     try {
                         buffers.add(new HistoricalBuffer(buffer));
                     } catch (IOException ex) {
@@ -267,18 +272,7 @@
                 }
             }
         } finally {
-            return nFinishDumpInMemory(dump);
-        }
-    }
-
-    private ParcelFileDescriptor getPfd(MemoryFile file) {
-        try {
-            if (!file.getFileDescriptor().valid()) {
-                throw new IllegalStateException("Invalid file descriptor");
-            }
-            return ParcelFileDescriptor.dup(file.getFileDescriptor());
-        } catch (IOException ex) {
-            throw new IllegalStateException("Failed to get PFD from memory file", ex);
+            nFinishDumpInMemory(dump, pulledData, lastFullDay);
         }
     }
 
@@ -286,7 +280,7 @@
             int uid, int pid, String packageName, long versionCode) throws RemoteException {
         ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
         scheduleRotateLocked();
-        return getPfd(buffer.mProcessBuffer);
+        return buffer.getPfd();
     }
 
     private Calendar normalizeDate(long timestamp) {
@@ -301,13 +295,15 @@
 
     private File pathForApp(BufferInfo info) {
         String subPath = String.format("%d/%s/%d/total",
-                normalizeDate(info.startTime).getTimeInMillis(), info.packageName, info.versionCode);
+                normalizeDate(info.mStartTime).getTimeInMillis(), info.mPackageName,
+                info.mVersionCode);
         return new File(mGraphicsStatsDir, subPath);
     }
 
     private void saveBuffer(HistoricalBuffer buffer) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "saving graphicsstats for " + buffer.mInfo.packageName);
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+                    "saving graphicsstats for " + buffer.mInfo.mPackageName);
         }
         synchronized (mFileAccessLock) {
             File path = pathForApp(buffer.mInfo);
@@ -317,8 +313,9 @@
                 Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
                 return;
             }
-            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.packageName, buffer.mInfo.versionCode,
-                    buffer.mInfo.startTime, buffer.mInfo.endTime, buffer.mData);
+            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.mPackageName,
+                    buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
+                    buffer.mData);
         }
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
@@ -365,7 +362,7 @@
             HistoricalBuffer data = new HistoricalBuffer(buffer);
             Message.obtain(mWriteOutHandler, SAVE_BUFFER, data).sendToTarget();
         } catch (IOException e) {
-            Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.packageName, e);
+            Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.mPackageName, e);
         }
         buffer.closeAllBuffers();
     }
@@ -386,7 +383,7 @@
             if (buffer.mPid == pid
                     && buffer.mUid == uid) {
                 // If the buffer is too old we remove it and return a new one
-                if (buffer.mInfo.startTime < today) {
+                if (buffer.mInfo.mStartTime < today) {
                     buffer.binderDied();
                     break;
                 } else {
@@ -410,8 +407,8 @@
             HistoricalBuffer buffer = buffers.get(i);
             File path = pathForApp(buffer.mInfo);
             skipFiles.add(path);
-            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.packageName,
-                    buffer.mInfo.versionCode,  buffer.mInfo.startTime, buffer.mInfo.endTime,
+            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.mPackageName,
+                    buffer.mInfo.mVersionCode,  buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
                     buffer.mData);
         }
         return skipFiles;
@@ -478,20 +475,20 @@
             long versionCode, long startTime, long endTime, byte[] data);
     private static native void nAddToDump(long dump, String path);
     private static native void nFinishDump(long dump);
-    private static native long nFinishDumpInMemory(long dump);
+    private static native void nFinishDumpInMemory(long dump, long pulledData, boolean lastFullDay);
     private static native void nSaveBuffer(String path, String packageName, long versionCode,
             long startTime, long endTime, byte[] data);
 
     private final class BufferInfo {
-        final String packageName;
-        final long versionCode;
-        long startTime;
-        long endTime;
+        final String mPackageName;
+        final long mVersionCode;
+        long mStartTime;
+        long mEndTime;
 
         BufferInfo(String packageName, long versionCode, long startTime) {
-            this.packageName = packageName;
-            this.versionCode = versionCode;
-            this.startTime = startTime;
+            this.mPackageName = packageName;
+            this.mVersionCode = versionCode;
+            this.mStartTime = startTime;
         }
     }
 
@@ -501,7 +498,8 @@
         final int mPid;
         final IGraphicsStatsCallback mCallback;
         final IBinder mToken;
-        MemoryFile mProcessBuffer;
+        SharedMemory mProcessBuffer;
+        ByteBuffer mMapping;
 
         ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
                 long versionCode)
@@ -512,8 +510,14 @@
             mCallback = token;
             mToken = mCallback.asBinder();
             mToken.linkToDeath(this, 0);
-            mProcessBuffer = new MemoryFile("GFXStats-" + pid, ASHMEM_SIZE);
-            mProcessBuffer.writeBytes(ZERO_DATA, 0, 0, ASHMEM_SIZE);
+            try {
+                mProcessBuffer = SharedMemory.create("GFXStats-" + pid, mAshmemSize);
+                mMapping = mProcessBuffer.mapReadWrite();
+            } catch (ErrnoException ex) {
+                ex.rethrowAsIOException();
+            }
+            mMapping.position(0);
+            mMapping.put(mZeroData, 0, mAshmemSize);
         }
 
         @Override
@@ -523,20 +527,40 @@
         }
 
         void closeAllBuffers() {
+            if (mMapping != null) {
+                SharedMemory.unmap(mMapping);
+                mMapping = null;
+            }
             if (mProcessBuffer != null) {
                 mProcessBuffer.close();
                 mProcessBuffer = null;
             }
         }
+
+        ParcelFileDescriptor getPfd() {
+            try {
+                return mProcessBuffer.getFdDup();
+            } catch (IOException ex) {
+                throw new IllegalStateException("Failed to get PFD from memory file", ex);
+            }
+        }
+
+        void readBytes(byte[] buffer, int count) throws IOException  {
+            if (mMapping == null) {
+                throw new IOException("SharedMemory has been deactivated");
+            }
+            mMapping.position(0);
+            mMapping.get(buffer, 0, count);
+        }
     }
 
     private final class HistoricalBuffer {
         final BufferInfo mInfo;
-        final byte[] mData = new byte[ASHMEM_SIZE];
+        final byte[] mData = new byte[mAshmemSize];
         HistoricalBuffer(ActiveBuffer active) throws IOException {
             mInfo = active.mInfo;
-            mInfo.endTime = System.currentTimeMillis();
-            active.mProcessBuffer.readBytes(mData, 0, 0, ASHMEM_SIZE);
+            mInfo.mEndTime = System.currentTimeMillis();
+            active.readBytes(mData, mAshmemSize);
         }
     }
 }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 51270f5..301d1af 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -92,9 +92,12 @@
                 "libandroidfw",
                 "libcrypto",
                 "libsync",
+                "libstatspull",
+                "libstatssocket",
             ],
             static_libs: [
                 "libEGL_blobCache",
+                "libprotoutil",
             ],
         },
         host: {
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index c418617..644d5fb 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -26,9 +26,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <algorithm>
-#include <map>
-#include <vector>
+#include <android/util/ProtoOutputStream.h>
+#include <stats_event.h>
+#include <statslog.h>
 
 #include "JankTracker.h"
 #include "protos/graphicsstats.pb.h"
@@ -61,7 +61,7 @@
         }
     }
     bool valid() { return mFd != -1; }
-    operator int() { return mFd; }  // NOLINT(google-explicit-constructor)
+    operator int() { return mFd; } // NOLINT(google-explicit-constructor)
 
 private:
     int mFd;
@@ -485,79 +485,82 @@
     delete dump;
 }
 
-class MemOutputStreamLite : public io::ZeroCopyOutputStream {
-public:
-    explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {}
-    virtual ~MemOutputStreamLite() {}
+using namespace google::protobuf;
 
-    virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
+// Field ids taken from FrameTimingHistogram message in atoms.proto
+#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
+#define FRAME_COUNTS_FIELD_NUMBER 2
 
-    virtual void BackUp(int count) override { mImpl.BackUp(count); }
-
-    virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
-
-    bool Flush() { return mImpl.Flush(); }
-
-    void copyData(const DumpMemoryFn& reader, void* param1, void* param2) {
-        int bufferOffset = 0;
-        int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize;
-        int totalDataLeft = totalSize;
-        for (auto& it : mCopyAdapter.mBuffers) {
-            int bufferSize = std::min(totalDataLeft, (int)it.size());  // last buffer is not full
-            reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2);
-            bufferOffset += bufferSize;
-            totalDataLeft -= bufferSize;
-        }
+static void writeCpuHistogram(AStatsEvent* event,
+                              const uirenderer::protos::GraphicsStatsProto& stat) {
+    util::ProtoOutputStream proto;
+    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+        auto& bucket = stat.histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+                    (int)bucket.render_millis());
     }
-
-private:
-    struct MemAdapter : public io::CopyingOutputStream {
-        // Data is stored in an array of buffers.
-        // JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer.
-        std::vector<std::vector<unsigned char>> mBuffers;
-        int mBuffersSize = 0;                     // total bytes allocated in mBuffers
-        int mCurrentBufferUnusedSize = 0;         // unused bytes in the last buffer mBuffers.back()
-        unsigned char* mCurrentBuffer = nullptr;  // pointer to next free byte in mBuffers.back()
-
-        explicit MemAdapter() {}
-        virtual ~MemAdapter() {}
-
-        virtual bool Write(const void* buffer, int size) override {
-            while (size > 0) {
-                if (0 == mCurrentBufferUnusedSize) {
-                    mCurrentBufferUnusedSize =
-                            std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000);
-                    mBuffers.emplace_back();
-                    mBuffers.back().resize(mCurrentBufferUnusedSize);
-                    mCurrentBuffer = mBuffers.back().data();
-                    mBuffersSize += mCurrentBufferUnusedSize;
-                }
-                int dataMoved = std::min(mCurrentBufferUnusedSize, size);
-                memcpy(mCurrentBuffer, buffer, dataMoved);
-                mCurrentBufferUnusedSize -= dataMoved;
-                mCurrentBuffer += dataMoved;
-                buffer = reinterpret_cast<const unsigned char*>(buffer) + dataMoved;
-                size -= dataMoved;
-            }
-            return true;
-        }
-    };
-
-    MemOutputStreamLite::MemAdapter mCopyAdapter;
-    io::CopyingOutputStreamAdaptor mImpl;
-};
-
-void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
-                                              void* param2) {
-    MemOutputStreamLite stream;
-    dump->updateProto();
-    bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush();
-    delete dump;
-    if (!success) {
-        return;
+    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+        auto& bucket = stat.histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
+                    (long long)bucket.frame_count());
     }
-    stream.copyData(reader, param1, param2);
+    std::vector<uint8_t> outVector;
+    proto.serializeToVector(&outVector);
+    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
 }
 
+static void writeGpuHistogram(AStatsEvent* event,
+                              const uirenderer::protos::GraphicsStatsProto& stat) {
+    util::ProtoOutputStream proto;
+    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+        auto& bucket = stat.gpu_histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+                    (int)bucket.render_millis());
+    }
+    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+        auto& bucket = stat.gpu_histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
+                    (long long)bucket.frame_count());
+    }
+    std::vector<uint8_t> outVector;
+    proto.serializeToVector(&outVector);
+    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
+}
+
+
+void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data,
+                                              bool lastFullDay) {
+    dump->updateProto();
+    auto& serviceDump = dump->proto();
+    for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
+        auto& stat = serviceDump.stats(stat_index);
+        AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+        AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
+        AStatsEvent_writeString(event, stat.package_name().c_str());
+        AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
+        AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
+        AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
+        AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
+        writeCpuHistogram(event, stat);
+        writeGpuHistogram(event, stat);
+        // TODO: fill in UI mainline module version, when the feature is available.
+        AStatsEvent_writeInt64(event, (int64_t)0);
+        AStatsEvent_writeBool(event, !lastFullDay);
+        AStatsEvent_build(event);
+    }
+}
+
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
index 4bed9633..59e21d0 100644
--- a/libs/hwui/service/GraphicsStatsService.h
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -20,6 +20,7 @@
 
 #include "JankTracker.h"
 #include "utils/Macros.h"
+#include <stats_pull_atom_callback.h>
 
 namespace android {
 namespace uirenderer {
@@ -27,9 +28,6 @@
 class GraphicsStatsProto;
 }
 
-typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize,
-                             void* param1, void* param2);
-
 /*
  * The exported entry points used by GraphicsStatsService.java in f/b/services/core
  *
@@ -56,8 +54,8 @@
                                       int64_t startTime, int64_t endTime, const ProfileData* data);
     ANDROID_API static void addToDump(Dump* dump, const std::string& path);
     ANDROID_API static void finishDump(Dump* dump);
-    ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
-                                               void* param2);
+    ANDROID_API static void finishDumpInMemory(Dump* dump, AStatsEventList* data,
+                                               bool lastFullDay);
 
     // Visible for testing
     static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output);
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index e4348f2..3b494e9 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -251,19 +251,24 @@
 void PointerController::setPresentation(Presentation presentation) {
     AutoMutex _l(mLock);
 
-    if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                &mLocked.animationResources, mLocked.viewport.displayId);
+    if (mLocked.presentation == presentation) {
+        return;
     }
 
-    if (mLocked.presentation != presentation) {
-        mLocked.presentation = presentation;
-        mLocked.presentationChanged = true;
+    mLocked.presentation = presentation;
+    mLocked.presentationChanged = true;
 
-        if (presentation != PRESENTATION_SPOT) {
-            fadeOutAndReleaseAllSpotsLocked();
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
+    if (presentation == PRESENTATION_POINTER) {
+        if (mLocked.additionalMouseResources.empty()) {
+            mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                                  &mLocked.animationResources,
+                                                  mLocked.viewport.displayId);
         }
-
+        fadeOutAndReleaseAllSpotsLocked();
         updatePointerLocked();
     }
 }
@@ -285,6 +290,9 @@
 #endif
 
     AutoMutex _l(mLock);
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
 
     std::vector<Spot*> newSpots;
     std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
@@ -331,6 +339,9 @@
 #endif
 
     AutoMutex _l(mLock);
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
 
     fadeOutAndReleaseAllSpotsLocked();
 }
@@ -752,6 +763,10 @@
 }
 
 void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
     mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
     mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
 
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index b36406d..a157426 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -39,8 +39,8 @@
 
 using ::testing::AllOf;
 using ::testing::Field;
-using ::testing::NiceMock;
 using ::testing::Mock;
+using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::Test;
 
@@ -57,12 +57,20 @@
     virtual int32_t getDefaultPointerIconId() override;
     virtual int32_t getCustomPointerIconId() override;
 
+    bool allResourcesAreLoaded();
+    bool noResourcesAreLoaded();
+
 private:
     void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+
+    bool pointerIconLoaded{false};
+    bool pointerResourcesLoaded{false};
+    bool additionalMouseResourcesLoaded{false};
 };
 
 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
     loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+    pointerIconLoaded = true;
 }
 
 void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
@@ -70,6 +78,7 @@
     loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
     loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
     loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+    pointerResourcesLoaded = true;
 }
 
 void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
@@ -91,6 +100,8 @@
     anim.durationPerFrame = 10;
     (*outResources)[cursorType] = icon;
     (*outAnimationResources)[cursorType] = anim;
+
+    additionalMouseResourcesLoaded = true;
 }
 
 int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
@@ -101,18 +112,27 @@
     return CURSOR_TYPE_CUSTOM;
 }
 
+bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
+    return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
+}
+
+bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
+    return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
+}
+
 void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
     icon->style = type;
     std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
     icon->hotSpotX = hotSpot.first;
     icon->hotSpotY = hotSpot.second;
 }
-
 class PointerControllerTest : public Test {
 protected:
     PointerControllerTest();
     ~PointerControllerTest();
 
+    void ensureDisplayViewportIsSet();
+
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
     sp<MockSpriteController> mSpriteController;
@@ -141,7 +161,14 @@
             .WillOnce(Return(mPointerSprite));
 
     mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+}
 
+PointerControllerTest::~PointerControllerTest() {
+    mRunning.store(false, std::memory_order_relaxed);
+    mThread.join();
+}
+
+void PointerControllerTest::ensureDisplayViewportIsSet() {
     DisplayViewport viewport;
     viewport.displayId = ADISPLAY_ID_DEFAULT;
     viewport.logicalRight = 1600;
@@ -151,11 +178,9 @@
     viewport.deviceWidth = 400;
     viewport.deviceHeight = 300;
     mPointerController->setDisplayViewport(viewport);
-}
 
-PointerControllerTest::~PointerControllerTest() {
-    mRunning.store(false, std::memory_order_relaxed);
-    mThread.join();
+    // The first call to setDisplayViewport should trigger the loading of the necessary resources.
+    EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
 }
 
 void PointerControllerTest::loopThread() {
@@ -167,6 +192,7 @@
 }
 
 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+    ensureDisplayViewportIsSet();
     mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
 
     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
@@ -181,6 +207,7 @@
 }
 
 TEST_F(PointerControllerTest, updatePointerIcon) {
+    ensureDisplayViewportIsSet();
     mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
 
     int32_t type = CURSOR_TYPE_ADDITIONAL;
@@ -196,6 +223,7 @@
 }
 
 TEST_F(PointerControllerTest, setCustomPointerIcon) {
+    ensureDisplayViewportIsSet();
     mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
 
     int32_t style = CURSOR_TYPE_CUSTOM;
@@ -217,4 +245,18 @@
     mPointerController->setCustomPointerIcon(icon);
 }
 
+TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
+    mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+    mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
+    mPointerController->clearSpots();
+    mPointerController->setPosition(1.0f, 1.0f);
+    mPointerController->move(1.0f, 1.0f);
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+
+    EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
+
+    ensureDisplayViewportIsSet();
+}
+
 }  // namespace android
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index 9b047ca..1e62107 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -20,7 +20,6 @@
         ":IDropBoxManagerService.aidl",
         "src/content/ComponentName.cpp",
         "src/os/DropBoxManager.cpp",
-        "src/os/StatsDimensionsValue.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/services/include/android/os/StatsDimensionsValue.h b/libs/services/include/android/os/StatsDimensionsValue.h
deleted file mode 100644
index cc0b056..0000000
--- a/libs/services/include/android/os/StatsDimensionsValue.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef STATS_DIMENSIONS_VALUE_H
-#define STATS_DIMENSIONS_VALUE_H
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <binder/Status.h>
-#include <utils/String16.h>
-#include <vector>
-
-namespace android {
-namespace os {
-
-// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java.
-class StatsDimensionsValue : public android::Parcelable {
-public:
-    StatsDimensionsValue();
-
-    StatsDimensionsValue(int32_t field, String16 value);
-    StatsDimensionsValue(int32_t field, int32_t value);
-    StatsDimensionsValue(int32_t field, int64_t value);
-    StatsDimensionsValue(int32_t field, bool value);
-    StatsDimensionsValue(int32_t field, float value);
-    StatsDimensionsValue(int32_t field, std::vector<StatsDimensionsValue> value);
-
-    virtual ~StatsDimensionsValue();
-
-    virtual android::status_t writeToParcel(android::Parcel* out) const override;
-    virtual android::status_t readFromParcel(const android::Parcel* in) override;
-
-private:
-    // Keep constants in sync with android/os/StatsDimensionsValue.java
-    // and stats_log.proto's DimensionValue.
-    static const int kStrValueType = 2;
-    static const int kIntValueType = 3;
-    static const int kLongValueType = 4;
-    static const int kBoolValueType = 5;
-    static const int kFloatValueType = 6;
-    static const int kTupleValueType = 7;
-
-    int32_t mField;
-    int32_t mValueType;
-
-    // This isn't very clever, but it isn't used for long-term storage, so it'll do.
-    String16 mStrValue;
-    int32_t mIntValue;
-    int64_t mLongValue;
-    bool mBoolValue;
-    float mFloatValue;
-    std::vector<StatsDimensionsValue> mTupleValue;
-};
-
-}  // namespace os
-}  // namespace android
-
-#endif // STATS_DIMENSIONS_VALUE_H
diff --git a/libs/services/src/os/StatsDimensionsValue.cpp b/libs/services/src/os/StatsDimensionsValue.cpp
deleted file mode 100644
index 0052e0b..0000000
--- a/libs/services/src/os/StatsDimensionsValue.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "StatsDimensionsValue"
-
-#include "android/os/StatsDimensionsValue.h"
-
-#include <cutils/log.h>
-
-using android::Parcel;
-using android::Parcelable;
-using android::status_t;
-using std::vector;
-
-namespace android {
-namespace os {
-
-StatsDimensionsValue::StatsDimensionsValue() {};
-
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) :
-    mField(field),
-    mValueType(kStrValueType),
-    mStrValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) :
-    mField(field),
-    mValueType(kIntValueType),
-    mIntValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) :
-    mField(field),
-    mValueType(kLongValueType),
-    mLongValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) :
-    mField(field),
-    mValueType(kBoolValueType),
-    mBoolValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) :
-    mField(field),
-    mValueType(kFloatValueType),
-    mFloatValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector<StatsDimensionsValue> value) :
-    mField(field),
-    mValueType(kTupleValueType),
-    mTupleValue(value) {
-}
-
-StatsDimensionsValue::~StatsDimensionsValue() {}
-
-status_t
-StatsDimensionsValue::writeToParcel(Parcel* out) const {
-    status_t err ;
-
-    err = out->writeInt32(mField);
-    if (err != NO_ERROR) {
-        return err;
-    }
-    err = out->writeInt32(mValueType);
-    if (err != NO_ERROR) {
-        return err;
-    }
-    switch (mValueType) {
-        case kStrValueType:
-            err = out->writeString16(mStrValue);
-            break;
-        case kIntValueType:
-            err = out->writeInt32(mIntValue);
-            break;
-        case kLongValueType:
-            err = out->writeInt64(mLongValue);
-            break;
-        case kBoolValueType:
-            err = out->writeBool(mBoolValue);
-            break;
-        case kFloatValueType:
-            err = out->writeFloat(mFloatValue);
-            break;
-        case kTupleValueType:
-            {
-                int sz = mTupleValue.size();
-                err = out->writeInt32(sz);
-                if (err != NO_ERROR) {
-                    return err;
-                }
-                for (int i = 0; i < sz; ++i) {
-                    err = mTupleValue[i].writeToParcel(out);
-                    if (err != NO_ERROR) {
-                        return err;
-                    }
-                }
-            }
-            break;
-        default:
-            err = UNKNOWN_ERROR;
-            break;
-    }
-    return err;
-}
-
-status_t
-StatsDimensionsValue::readFromParcel(const Parcel* in)
-{
-    // Implement me if desired. We don't currently use this.
-    ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented.");
-    (void)in; // To prevent compile error of unused parameter 'in'
-    return UNKNOWN_ERROR;
-}
-
-}  // namespace os
-}  // namespace android
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/location/java/android/location/GnssRequest.aidl
similarity index 76%
copy from core/java/android/os/StatsDimensionsValue.aidl
copy to location/java/android/location/GnssRequest.aidl
index 81a14a4..581abcc 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/location/java/android/location/GnssRequest.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.location;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+/**
+ * @hide
+ */
+parcelable GnssRequest;
diff --git a/location/java/android/location/GnssRequest.java b/location/java/android/location/GnssRequest.java
new file mode 100644
index 0000000..2afb265
--- /dev/null
+++ b/location/java/android/location/GnssRequest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains extra parameters to pass to a GNSS provider implementation.
+ * @hide
+ */
+@SystemApi
+public final class GnssRequest implements Parcelable {
+    private final boolean mFullTracking;
+
+    /**
+     * Creates a {@link GnssRequest} with a full list of parameters.
+     */
+    private GnssRequest(boolean fullTracking) {
+        mFullTracking = fullTracking;
+    }
+
+    /**
+     * Represents whether to enable full GNSS tracking.
+     *
+     * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
+     * discontinuities are expected, and when supported, carrier phase should be continuous in
+     * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+     * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
+     * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
+     * duty cycling, constellations and frequency limits, etc.
+     */
+    public boolean isFullTracking() {
+        return mFullTracking;
+    }
+
+    @NonNull
+    public static final Creator<GnssRequest> CREATOR =
+            new Creator<GnssRequest>() {
+                @Override
+                @NonNull
+                public GnssRequest createFromParcel(@NonNull Parcel parcel) {
+                    return new GnssRequest(parcel.readBoolean());
+                }
+
+                @Override
+                public GnssRequest[] newArray(int i) {
+                    return new GnssRequest[i];
+                }
+            };
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("GnssRequest[");
+        s.append("FullTracking=").append(mFullTracking);
+        s.append(']');
+        return s.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (!(obj instanceof GnssRequest)) return false;
+
+        GnssRequest other = (GnssRequest) obj;
+        if (mFullTracking != other.mFullTracking) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return mFullTracking ? 1 : 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeBoolean(mFullTracking);
+    }
+
+    /** Builder for {@link GnssRequest} */
+    public static final class Builder {
+        private boolean mFullTracking;
+
+        /**
+         * Constructs a {@link Builder} instance.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a {@link Builder} instance by copying a {@link GnssRequest}.
+         */
+        public Builder(@NonNull GnssRequest request) {
+            mFullTracking = request.isFullTracking();
+        }
+
+        /**
+         * Set the value of whether to enable full GNSS tracking, which is false by default.
+         *
+         * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
+         * discontinuities are expected, and when supported, carrier phase should be continuous in
+         * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+         * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
+         * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
+         * duty cycling, constellations and frequency limits, etc.
+         *
+         * <p>Full tracking requests always override non-full tracking requests. If any full
+         * tracking request occurs, all listeners on the device will receive full tracking GNSS
+         * measurements.
+         */
+        @NonNull public Builder setFullTracking(boolean value) {
+            mFullTracking = value;
+            return this;
+        }
+
+        /** Builds a {@link GnssRequest} instance as specified by this builder. */
+        @NonNull
+        public GnssRequest build() {
+            return new GnssRequest(mFullTracking);
+        }
+    }
+}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1c10edb..7e6486c 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2196,6 +2196,30 @@
     }
 
     /**
+     * Registers a GNSS Measurement callback.
+     *
+     * @param request  extra parameters to pass to GNSS measurement provider. For example, if {@link
+     *                 GnssRequest#isFullTrackingEnabled()} is true, GNSS chipset switches off duty
+     *                 cycling.
+     * @param executor the executor that the callback runs on.
+     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * @throws IllegalArgumentException if request is null
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException        if the ACCESS_FINE_LOCATION permission is not present
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, LOCATION_HARDWARE})
+    public boolean registerGnssMeasurementsCallback(
+            @NonNull GnssRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull GnssMeasurementsEvent.Callback callback) {
+        throw new RuntimeException();
+    }
+
+    /**
      * Injects GNSS measurement corrections into the GNSS chipset.
      *
      * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index c1b17d3..63de0334 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -41,8 +41,7 @@
             FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
             FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
             FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
-            FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
-            FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
+            FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_MER,
             FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
             FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
     @Retention(RetentionPolicy.SOURCE)
@@ -125,18 +124,6 @@
     public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
             Constants.FrontendStatusType.LAYER_ERROR;
     /**
-     * CN value by VBER.
-     */
-    public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
-    /**
-     * CN value by LBER.
-     */
-    public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
-    /**
-     * CN value by XER.
-     */
-    public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
-    /**
      * Modulation Error Ratio.
      */
     public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
@@ -223,9 +210,6 @@
     private Integer mAgc;
     private Boolean mIsLnaOn;
     private boolean[] mIsLayerErrors;
-    private Integer mVberCn;
-    private Integer mLberCn;
-    private Integer mXerCn;
     private Integer mMer;
     private Integer mFreqOffset;
     private Integer mHierarchy;
@@ -403,33 +387,6 @@
         return mIsLayerErrors;
     }
     /**
-     * Gets CN value by VBER in thousandths of a deciBel (0.001dB).
-     */
-    public int getVberCn() {
-        if (mVberCn == null) {
-            throw new IllegalStateException();
-        }
-        return mVberCn;
-    }
-    /**
-     * Gets CN value by LBER in thousandths of a deciBel (0.001dB).
-     */
-    public int getLberCn() {
-        if (mLberCn == null) {
-            throw new IllegalStateException();
-        }
-        return mLberCn;
-    }
-    /**
-     * Gets CN value by XER in thousandths of a deciBel (0.001dB).
-     */
-    public int getXerCn() {
-        if (mXerCn == null) {
-            throw new IllegalStateException();
-        }
-        return mXerCn;
-    }
-    /**
      * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB).
      */
     public int getMer() {
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index f90144b..8105c74 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -67,7 +67,7 @@
     /** Frontend hierarchy. */
     void onHierarchy(@DvbtFrontendSettings.Hierarchy int hierarchy);
 
-    /** Frontend hierarchy. */
+    /** Frontend signal type. */
     void onSignalType(@AnalogFrontendSettings.SignalType int signalType);
 
 }
diff --git a/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml b/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
index a06dc54..c4d8f35 100644
--- a/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
+++ b/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
@@ -29,7 +29,7 @@
         <service android:enabled="true"
                  android:name="com.android.incremental.nativeadb.NativeAdbDataLoaderService"
                  android:label="@string/app_name"
-                 android:exported="true">
+                 android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.LOAD_DATA" />
            </intent-filter>
diff --git a/packages/SystemUI/res/drawable-nodpi/android_11_dial.xml b/packages/SystemUI/res/drawable-nodpi/android_11_dial.xml
new file mode 100644
index 0000000..73fd37f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/android_11_dial.xml
@@ -0,0 +1,63 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+  <path
+      android:pathData="M77.773,51.064h-1.583c-0.217,0 -0.393,-0.176 -0.393,-0.393v-1.46c0,-0.217 0.176,-0.393 0.393,-0.393h3.466c0.217,0 0.393,0.176 0.393,0.393v9.921c0,0.217 -0.176,0.393 -0.393,0.393h-1.49c-0.217,0 -0.393,-0.176 -0.393,-0.393V51.064z"
+      android:fillColor="#F86734"/>
+  <path
+      android:pathData="M83.598,51.064h-1.583c-0.217,0 -0.393,-0.176 -0.393,-0.393v-1.46c0,-0.217 0.176,-0.393 0.393,-0.393h3.466c0.217,0 0.393,0.176 0.393,0.393v9.921c0,0.217 -0.176,0.393 -0.393,0.393h-1.49c-0.217,0 -0.393,-0.176 -0.393,-0.393V51.064z"
+      android:fillColor="#F86734"/>
+  <path
+      android:pathData="M70.044,75.974m-0.644,0a0.644,0.644 0,1 1,1.288 0a0.644,0.644 0,1 1,-1.288 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M56.896,80.985m-0.718,0a0.718,0.718 0,1 1,1.436 0a0.718,0.718 0,1 1,-1.436 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M43.408,78.881m-0.795,0a0.795,0.795 0,1 1,1.59 0a0.795,0.795 0,1 1,-1.59 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M32.419,70.115m-0.874,0a0.874,0.874 0,1 1,1.748 0a0.874,0.874 0,1 1,-1.748 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M27.306,56.992m-0.954,0a0.954,0.954 0,1 1,1.908 0a0.954,0.954 0,1 1,-1.908 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M29.313,43.489m-1.036,0a1.036,1.036 0,1 1,2.072 0a1.036,1.036 0,1 1,-2.072 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M37.988,32.445m-1.118,0a1.118,1.118 0,1 1,2.236 0a1.118,1.118 0,1 1,-2.236 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M51.137,27.064m-1.201,0a1.201,1.201 0,1 1,2.402 0a1.201,1.201 0,1 1,-2.402 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M64.553,28.868m-1.284,0a1.284,1.284 0,1 1,2.568 0a1.284,1.284 0,1 1,-2.568 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M75.522,37.652m-1.368,0a1.368,1.368 0,1 1,2.736 0a1.368,1.368 0,1 1,-2.736 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M87.942,115.052l-47.557,-47.557l26.869,-26.87l47.557,47.558z">
+    <aapt:attr name="android:fillColor">
+      <gradient 
+          android:startY="56.087"
+          android:startX="55.8464"
+          android:endY="100.0297"
+          android:endX="99.7891"
+          android:type="linear">
+        <item android:offset="0" android:color="#3F000000"/>
+        <item android:offset="1" android:color="#00000000"/>
+      </gradient>
+    </aapt:attr>
+  </path>
+  <path
+      android:pathData="M53.928,54.17m-18.999,0a18.999,18.999 0,1 1,37.998 0a18.999,18.999 0,1 1,-37.998 0"
+      android:fillColor="#3ddc84"/>
+  <path
+      android:pathData="M66.353,54.17m-3.185,0a3.185,3.185 0,1 1,6.37 0a3.185,3.185 0,1 1,-6.37 0"
+      android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon.xml b/packages/SystemUI/res/drawable-nodpi/icon.xml
index 7a68c03..7f8d4fa 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon.xml
@@ -15,5 +15,5 @@
 -->
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@drawable/icon_bg"/>
-    <foreground android:drawable="@drawable/q"/>
+    <foreground android:drawable="@drawable/android_11_dial"/>
 </adaptive-icon>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
index 2a54dfa..31b2a7f 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
@@ -14,5 +14,5 @@
     limitations under the License.
 -->
 <color xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="#77C360" />
+    android:color="#073042" />
 
diff --git a/packages/SystemUI/res/drawable-nodpi/q.xml b/packages/SystemUI/res/drawable-nodpi/q.xml
deleted file mode 100644
index 0f42d2e..0000000
--- a/packages/SystemUI/res/drawable-nodpi/q.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<!--
-Copyright (C) 2019 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="108dp"
-        android:height="108dp"
-        android:viewportWidth="108.0"
-        android:viewportHeight="108.0">
-    <group
-        android:name="scale"
-        android:pivotX="54" android:pivotY="54"
-        android:scaleX="0.9"
-        android:scaleY="0.9">
-        <group
-            android:name="nudge"
-            android:translateX="24"
-            android:translateY="23.5">
-            <path
-                android:name="tail"
-                android:fillColor="#FFFFFF"
-                android:pathData="M21.749674,34.122784l-9.431964,9.529709l-6.31771,-6.2529106l15.736504,-15.899582l64.765724,65.16436l-6.3046494,6.266083z"/>
-            <path
-                android:name="counter"
-                android:fillColor="#FFFFFF"
-                android:pathData="M30,9.32352941 C41.6954418,9.32352941 51.1764706,18.8045582 51.1764706,30.5 C51.1764706,42.1954418 41.6954418,51.6764706 30,51.6764706 C18.3045582,51.6764706 8.82352941,42.1954418 8.82352941,30.5 C8.82352941,18.8045582 18.3045582,9.32352941 30,9.32352941 L30,9.32352941 Z M30,0.5 C13.4314575,0.5 -5.53805368e-15,13.9314575 -7.10542736e-15,30.5 C-1.02401747e-14,47.0685425 13.4314575,60.5 30,60.5 C46.5685425,60.5 60,47.0685425 60,30.5 C59.9805514,13.9395201 46.5604799,0.519448617 30,0.5 Z"/>
-        </group>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d72ce5e..8de2df5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -487,9 +487,6 @@
     <!-- Whether or not to add a "people" notifications section -->
     <bool name="config_usePeopleFiltering">false</bool>
 
-    <!-- Package name for controls plugin -->
-    <string name="config_controlsPluginPackageName" translatable="false">com.android.systemui.controls.panel</string>
-
     <!-- Defines the blacklist for system icons.  That is to say, the icons in the status bar that
          are part of the blacklist are never displayed. Each item in the blacklist must be a string
          defined in core/res/res/config.xml to properly blacklist the icon.
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c4fa4e5..7a3395c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1090,7 +1090,7 @@
 
     <!--  Blur radius on status bar window and power menu  -->
     <dimen name="min_window_blur_radius">1px</dimen>
-    <dimen name="max_window_blur_radius">250px</dimen>
+    <dimen name="max_window_blur_radius">150px</dimen>
 
     <!-- How much into a DisplayCutout's bounds we can go, on each side -->
     <dimen name="display_cutout_margin_consumption">0px</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index f61f585..f48210c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -42,7 +42,6 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.animation.Animation;
-import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -251,9 +250,9 @@
             SliceItem item = rc.getSliceItem();
             final Uri itemTag = item.getSlice().getUri();
             // Try to reuse the view if already exists in the layout
-            KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
+            KeyguardSliceTextView button = mRow.findViewWithTag(itemTag);
             if (button == null) {
-                button = new KeyguardSliceButton(mContext);
+                button = new KeyguardSliceTextView(mContext);
                 button.setTextColor(blendedColor);
                 button.setTag(itemTag);
                 final int viewIndex = i - (mHasHeader ? 1 : 0);
@@ -316,8 +315,8 @@
         int childCount = mRow.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View v = mRow.getChildAt(i);
-            if (v instanceof Button) {
-                ((Button) v).setTextColor(blendedColor);
+            if (v instanceof TextView) {
+                ((TextView) v).setTextColor(blendedColor);
             }
         }
     }
@@ -500,8 +499,8 @@
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
                 View child = getChildAt(i);
-                if (child instanceof KeyguardSliceButton) {
-                    ((KeyguardSliceButton) child).setMaxWidth(width / childCount);
+                if (child instanceof KeyguardSliceTextView) {
+                    ((KeyguardSliceTextView) child).setMaxWidth(width / childCount);
                 }
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -527,13 +526,13 @@
      * Representation of an item that appears under the clock on main keyguard message.
      */
     @VisibleForTesting
-    static class KeyguardSliceButton extends Button implements
+    static class KeyguardSliceTextView extends TextView implements
             ConfigurationController.ConfigurationListener {
 
         @StyleRes
         private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
 
-        public KeyguardSliceButton(Context context) {
+        KeyguardSliceTextView(Context context) {
             super(context, null /* attrs */, 0 /* styleAttr */, sStyleId);
             onDensityOrFontScaleChanged();
             setEllipsize(TruncateAt.END);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c9c38d3..146f36b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -36,8 +36,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -49,8 +47,7 @@
 
 import dagger.Lazy;
 
-public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
-        PluginListener<GlobalActionsPanelPlugin> {
+public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
 
     private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
 
@@ -60,12 +57,9 @@
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
     private final BlurUtils mBlurUtils;
-    private GlobalActionsPanelPlugin mPlugin;
     private final CommandQueue mCommandQueue;
     private GlobalActionsDialog mGlobalActionsDialog;
     private boolean mDisabled;
-    private final PluginManager mPluginManager;
-    private final String mPluginPackageName;
 
     @Inject
     public GlobalActionsImpl(Context context, CommandQueue commandQueue,
@@ -74,7 +68,6 @@
         mGlobalActionsDialogLazy = globalActionsDialogLazy;
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mPluginManager = Dependency.get(PluginManager.class);
         mCommandQueue = commandQueue;
         mBlurUtils = blurUtils;
         mCommandQueue.addCallback(this);
@@ -82,17 +75,11 @@
                 .newExtension(GlobalActionsPanelPlugin.class)
                 .withPlugin(GlobalActionsPanelPlugin.class)
                 .build();
-        mPluginPackageName = mContext.getString(
-                com.android.systemui.R.string.config_controlsPluginPackageName);
-        mPluginManager.addPluginListener(
-                GlobalActionsPanelPlugin.ACTION, this, GlobalActionsPanelPlugin.class, true);
     }
 
     @Override
     public void destroy() {
         mCommandQueue.removeCallback(this);
-        mPluginManager.removePluginListener(this);
-        if (mPlugin != null) mPlugin.onDestroy();
         if (mGlobalActionsDialog != null) {
             mGlobalActionsDialog.destroy();
             mGlobalActionsDialog = null;
@@ -105,7 +92,7 @@
         mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
         mGlobalActionsDialog.showDialog(mKeyguardStateController.isShowing(),
                 mDeviceProvisionedController.isDeviceProvisioned(),
-                mPlugin != null ? mPlugin : mPanelExtension.get());
+                mPanelExtension.get());
         Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth();
     }
 
@@ -205,16 +192,4 @@
             mGlobalActionsDialog.dismissDialog();
         }
     }
-
-    @Override
-    public void onPluginConnected(GlobalActionsPanelPlugin plugin, Context pluginContext) {
-        if (pluginContext.getPackageName().equals(mPluginPackageName)) {
-            mPlugin = plugin;
-        }
-    }
-
-    @Override
-    public void onPluginDisconnected(GlobalActionsPanelPlugin plugin) {
-        mPlugin = null;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 6498b91..ae380b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -93,6 +93,7 @@
             return mIatm.startActivityAsUser(
                     mContext.getIApplicationThread() /*caller*/,
                     mContext.getBasePackageName() /*callingPackage*/,
+                    mContext.getFeatureId() /*callingFeatureId*/,
                     intent /*intent*/,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()) /*resolvedType*/,
                     null /*resultTo*/,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 001e094..42f8010 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.UserManager;
 import android.service.quicksettings.Tile;
+import android.util.Log;
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -200,6 +201,14 @@
             mCallbackInfo.numConnectedDevices = numDevices;
             refreshState(mCallbackInfo);
         }
+
+        @Override
+        public void onHotspotAvailabilityChanged(boolean available) {
+            if (!available) {
+                Log.d(TAG, "Tile removed. Hotspot no longer available");
+                mHost.removeTile(getTileSpec());
+            }
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index d3e44ea..72dfa18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -492,7 +492,7 @@
                     try {
                         result = ActivityTaskManager.getService().startActivityAsUser(
                                 null, getContext().getBasePackageName(),
-                                intent,
+                                getContext().getFeatureId(), intent,
                                 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
                                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
                                 UserHandle.CURRENT.getIdentifier());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c68d994..4f01cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2567,7 +2567,7 @@
             }
             try {
                 result = ActivityTaskManager.getService().startActivityAsUser(
-                        null, mContext.getBasePackageName(),
+                        null, mContext.getBasePackageName(), mContext.getFeatureId(),
                         intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                         null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 830b50e..8231f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -30,5 +30,6 @@
 
     interface Callback {
         void onHotspotChanged(boolean enabled, int numDevices);
+        default void onHotspotAvailabilityChanged(boolean available) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index df9c3f4..d090404 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.TetheringManager;
 import android.net.wifi.WifiClient;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
@@ -26,6 +27,8 @@
 import android.os.UserManager;
 import android.util.Log;
 
+import com.android.internal.util.ConcurrentUtils;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 
 import java.io.FileDescriptor;
@@ -46,36 +49,63 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
-    private final ConnectivityManager mConnectivityManager;
+    private final TetheringManager mTetheringManager;
     private final WifiManager mWifiManager;
     private final Handler mMainHandler;
     private final Context mContext;
 
     private int mHotspotState;
     private volatile int mNumConnectedDevices;
+    private volatile boolean mIsTetheringSupported;
+    private volatile boolean mHasTetherableWifiRegexs;
     private boolean mWaitingForTerminalState;
 
+    private TetheringManager.TetheringEventCallback mTetheringCallback =
+            new TetheringManager.TetheringEventCallback() {
+                @Override
+                public void onTetheringSupported(boolean supported) {
+                    super.onTetheringSupported(supported);
+                    if (mIsTetheringSupported != supported) {
+                        mIsTetheringSupported = supported;
+                        fireHotspotAvailabilityChanged();
+                    }
+                }
+
+                @Override
+                public void onTetherableInterfaceRegexpsChanged(
+                        TetheringManager.TetheringInterfaceRegexps reg) {
+                    super.onTetherableInterfaceRegexpsChanged(reg);
+                    final boolean newValue = reg.getTetherableWifiRegexs().size() != 0;
+                    if (mHasTetherableWifiRegexs != newValue) {
+                        mHasTetherableWifiRegexs = newValue;
+                        fireHotspotAvailabilityChanged();
+                    }
+                }
+            };
+
     /**
      * Controller used to retrieve information related to a hotspot.
      */
     @Inject
-    public HotspotControllerImpl(Context context, @Main Handler mainHandler) {
+    public HotspotControllerImpl(Context context, @Main Handler mainHandler,
+            @Background Handler backgroundHandler) {
         mContext = context;
-        mConnectivityManager =
-                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mTetheringManager = context.getSystemService(TetheringManager.class);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         mMainHandler = mainHandler;
+        mTetheringManager.registerTetheringEventCallback(
+                new HandlerExecutor(backgroundHandler), mTetheringCallback);
     }
 
     @Override
     public boolean isHotspotSupported() {
-        return mConnectivityManager.isTetheringSupported()
-                && mConnectivityManager.getTetherableWifiRegexs().length != 0
+        return mIsTetheringSupported && mHasTetherableWifiRegexs
                 && UserManager.get(mContext).isUserAdmin(ActivityManager.getCurrentUser());
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("HotspotController state:");
+        pw.print("  available="); pw.println(isHotspotSupported());
         pw.print("  mHotspotState="); pw.println(stateToString(mHotspotState));
         pw.print("  mNumConnectedDevices="); pw.println(mNumConnectedDevices);
         pw.print("  mWaitingForTerminalState="); pw.println(mWaitingForTerminalState);
@@ -152,17 +182,18 @@
         if (enabled) {
             mWaitingForTerminalState = true;
             if (DEBUG) Log.d(TAG, "Starting tethering");
-            mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false,
-                    new ConnectivityManager.OnStartTetheringCallback() {
+            mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI,
+                    ConcurrentUtils.DIRECT_EXECUTOR,
+                    new TetheringManager.StartTetheringCallback() {
                         @Override
-                        public void onTetheringFailed() {
+                        public void onTetheringFailed(final int result) {
                             if (DEBUG) Log.d(TAG, "onTetheringFailed");
                             maybeResetSoftApState();
                             fireHotspotChangedCallback();
                         }
                     });
         } else {
-            mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+            mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
         }
     }
 
@@ -177,10 +208,25 @@
      * (as it can be blocked).
      */
     private void fireHotspotChangedCallback() {
+        List<Callback> list;
         synchronized (mCallbacks) {
-            for (Callback callback : mCallbacks) {
-                callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
-            }
+            list = new ArrayList<>(mCallbacks);
+        }
+        for (Callback callback : list) {
+            callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
+        }
+    }
+
+    /**
+     * Sends a hotspot available changed callback.
+     */
+    private void fireHotspotAvailabilityChanged() {
+        List<Callback> list;
+        synchronized (mCallbacks) {
+            list = new ArrayList<>(mCallbacks);
+        }
+        for (Callback callback : list) {
+            callback.onHotspotAvailabilityChanged(isHotspotSupported());
         }
     }
 
@@ -206,7 +252,7 @@
         switch (mHotspotState) {
             case WifiManager.WIFI_AP_STATE_FAILED:
                 // TODO(b/110697252): must be called to reset soft ap state after failure
-                mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+                mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
                 // Fall through
             case WifiManager.WIFI_AP_STATE_ENABLED:
             case WifiManager.WIFI_AP_STATE_DISABLED:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index d26ae6a..3439fe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -119,6 +119,7 @@
         doReturn(code).when(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
+                eq((String) null),
                 any(Intent.class),
                 eq((String) null),
                 eq((IBinder) null),
@@ -134,6 +135,7 @@
         verify(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
+                eq((String) null),
                 any(Intent.class),
                 eq((String) null),
                 eq((IBinder) null),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 631c580..cd91f22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -24,10 +26,12 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-import android.net.ConnectivityManager;
+import android.net.TetheringManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.os.UserManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -38,11 +42,14 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.concurrent.Executor;
 
 @SmallTest
@@ -51,13 +58,19 @@
 public class HotspotControllerImplTest extends SysuiTestCase {
 
     @Mock
-    private ConnectivityManager mConnectivityManager;
+    private TetheringManager mTetheringManager;
     @Mock
     private WifiManager mWifiManager;
     @Mock
+    private UserManager mUserManager;
+    @Mock
     private HotspotController.Callback mCallback1;
     @Mock
     private HotspotController.Callback mCallback2;
+    @Mock
+    private TetheringManager.TetheringInterfaceRegexps mTetheringInterfaceRegexps;
+    @Captor
+    private ArgumentCaptor<TetheringManager.TetheringEventCallback> mTetheringCallbackCaptor;
     private HotspotControllerImpl mController;
     private TestableLooper mLooper;
 
@@ -66,8 +79,13 @@
         MockitoAnnotations.initMocks(this);
         mLooper = TestableLooper.get(this);
 
-        mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
         mContext.addMockSystemService(WifiManager.class, mWifiManager);
+        mContext.addMockSystemService(TetheringManager.class, mTetheringManager);
+        mContext.addMockSystemService(UserManager.class, mUserManager);
+
+        when(mUserManager.isUserAdmin(anyInt())).thenReturn(true);
+        when(mTetheringInterfaceRegexps.getTetherableWifiRegexs()).thenReturn(
+                Collections.singletonList("test"));
 
         doAnswer((InvocationOnMock invocation) -> {
             ((WifiManager.SoftApCallback) invocation.getArgument(1))
@@ -76,7 +94,11 @@
         }).when(mWifiManager).registerSoftApCallback(any(Executor.class),
                 any(WifiManager.SoftApCallback.class));
 
-        mController = new HotspotControllerImpl(mContext, new Handler(mLooper.getLooper()));
+        Handler handler = new Handler(mLooper.getLooper());
+
+        mController = new HotspotControllerImpl(mContext, handler, handler);
+        verify(mTetheringManager)
+                .registerTetheringEventCallback(any(), mTetheringCallbackCaptor.capture());
     }
 
     @Test
@@ -117,4 +139,28 @@
         verify(mWifiManager, never()).unregisterSoftApCallback(any());
     }
 
+    @Test
+    public void testDefault_hotspotNotSupported() {
+        assertFalse(mController.isHotspotSupported());
+    }
+
+    @Test
+    public void testHotspotSupported_rightConditions() {
+        mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+        mTetheringCallbackCaptor.getValue()
+                .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
+
+        assertTrue(mController.isHotspotSupported());
+    }
+
+    @Test
+    public void testHotspotSupported_callbackCalledOnChange() {
+        mController.addCallback(mCallback1);
+        mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+        mTetheringCallbackCaptor.getValue()
+                .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
+
+        verify(mCallback1).onHotspotAvailabilityChanged(true);
+    }
+
 }
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
new file mode 100644
index 0000000..1fc48d2
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Maintains an inline suggestion autofill session.
+ *
+ * <p> This class is thread safe.
+ */
+final class InlineSuggestionSession {
+
+    private static final String TAG = "InlineSuggestionSession";
+    private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
+
+    @NonNull
+    private final InputMethodManagerInternal mInputMethodManagerInternal;
+    private final int mUserId;
+    @NonNull
+    private final ComponentName mComponentName;
+    @NonNull
+    private final Object mLock;
+
+    @GuardedBy("mLock")
+    @Nullable
+    private CompletableFuture<ImeResponse> mPendingImeResponse;
+
+    InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
+            int userId, ComponentName componentName) {
+        mInputMethodManagerInternal = inputMethodManagerInternal;
+        mUserId = userId;
+        mComponentName = componentName;
+        mLock = new Object();
+    }
+
+    public void createRequest(@NonNull AutofillId currentViewId) {
+        synchronized (mLock) {
+            cancelCurrentRequest();
+            mPendingImeResponse = new CompletableFuture<>();
+            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
+                    mUserId, mComponentName, currentViewId,
+                    new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse));
+        }
+    }
+
+    @Nullable
+    public ImeResponse waitAndGetImeResponse() {
+        CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
+        if (pendingImeResponse == null || pendingImeResponse.isCancelled()) {
+            return null;
+        }
+        try {
+            return pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
+        } catch (CancellationException e) {
+            Log.w(TAG, "Inline suggestions request cancelled");
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+
+    private void cancelCurrentRequest() {
+        CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
+        if (pendingImeResponse != null) {
+            pendingImeResponse.cancel(true);
+        }
+    }
+
+    @Nullable
+    @GuardedBy("mLock")
+    private CompletableFuture<ImeResponse> getPendingImeResponse() {
+        synchronized (mLock) {
+            return mPendingImeResponse;
+        }
+    }
+
+    private static final class InlineSuggestionsRequestCallbackImpl
+            extends IInlineSuggestionsRequestCallback.Stub {
+
+        private final CompletableFuture<ImeResponse> mResponse;
+
+        private InlineSuggestionsRequestCallbackImpl(CompletableFuture<ImeResponse> response) {
+            mResponse = response;
+        }
+
+        @Override
+        public void onInlineSuggestionsUnsupported() throws RemoteException {
+            if (sDebug) {
+                Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+            }
+            mResponse.cancel(true);
+        }
+
+        @Override
+        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+                IInlineSuggestionsResponseCallback callback) throws RemoteException {
+            if (sDebug) {
+                Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+            }
+            if (request != null && callback != null) {
+                mResponse.complete(new ImeResponse(request, callback));
+            } else {
+                mResponse.cancel(true);
+            }
+        }
+    }
+
+    /**
+     * A data class wrapping IME responses for the inline suggestion request.
+     */
+    public static class ImeResponse {
+        @NonNull
+        private final InlineSuggestionsRequest mRequest;
+
+        @NonNull
+        private final IInlineSuggestionsResponseCallback mCallback;
+
+        ImeResponse(@NonNull InlineSuggestionsRequest request,
+                @NonNull IInlineSuggestionsResponseCallback callback) {
+            mRequest = request;
+            mCallback = callback;
+        }
+
+        public InlineSuggestionsRequest getRequest() {
+            return mRequest;
+        }
+
+        public IInlineSuggestionsResponseCallback getCallback() {
+            return mCallback;
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a25d7353..53f85ea 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -100,7 +100,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInlineSuggestionsResponseCallback;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.InlineSuggestionFactory;
@@ -114,11 +113,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -159,9 +153,6 @@
     /** uid the session is for */
     public final int uid;
 
-    /** user id the session is for */
-    public final int userId;
-
     /** ID of the task associated with this session's activity */
     public final int taskId;
 
@@ -310,12 +301,8 @@
     @GuardedBy("mLock")
     private boolean mForAugmentedAutofillOnly;
 
-    @NonNull
-    private final InputMethodManagerInternal mInputMethodManagerInternal;
-
     @Nullable
-    @GuardedBy("mLock")
-    private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback;
+    private final InlineSuggestionSession mInlineSuggestionSession;
 
     /**
      * Receiver of assist data from the app's {@link Activity}.
@@ -416,12 +403,11 @@
                 final ArrayList<FillContext> contexts =
                         mergePreviousSessionLocked(/* forSave= */ false);
 
-                final InlineSuggestionsRequest suggestionsRequest =
-                        mInlineSuggestionsRequestCallback != null
-                                ? mInlineSuggestionsRequestCallback.getRequest() : null;
+                final InlineSuggestionSession.ImeResponse imeResponse =
+                        mInlineSuggestionSession.waitAndGetImeResponse();
 
                 request = new FillRequest(requestId, contexts, mClientState, flags,
-                        suggestionsRequest);
+                        imeResponse != null ? imeResponse.getRequest() : null);
             }
 
             if (mActivityToken != null) {
@@ -619,75 +605,12 @@
     private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
             int newState, int flags) {
         if (isInlineSuggestionsEnabled()) {
-            mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl();
-            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(userId,
-                    mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback);
+            mInlineSuggestionSession.createRequest(mCurrentViewId);
         }
 
         requestNewFillResponseLocked(viewState, newState, flags);
     }
 
-    private static final class InlineSuggestionsRequestCallbackImpl
-            extends IInlineSuggestionsRequestCallback.Stub {
-        private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
-
-        private final CompletableFuture<InlineSuggestionsRequest> mRequest;
-        private final CompletableFuture<IInlineSuggestionsResponseCallback> mResponseCallback;
-
-        private InlineSuggestionsRequestCallbackImpl() {
-            mRequest = new CompletableFuture<>();
-            mResponseCallback = new CompletableFuture<>();
-        }
-
-        @Override
-        public void onInlineSuggestionsUnsupported() throws RemoteException {
-            if (sDebug) {
-                Log.d(TAG, "inline suggestions request unsupported, "
-                        + "falling back to regular autofill");
-            }
-            mRequest.cancel(true);
-            mResponseCallback.cancel(true);
-        }
-
-        @Override
-        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
-                IInlineSuggestionsResponseCallback callback) throws RemoteException {
-            if (sDebug) {
-                Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
-            }
-            mRequest.complete(request);
-            mResponseCallback.complete(callback);
-        }
-
-        @Nullable
-        private InlineSuggestionsRequest getRequest() {
-            try {
-                return mRequest.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException e) {
-                Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
-            } catch (CancellationException e) {
-                Log.w(TAG, "Inline suggestions request cancelled");
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-            return null;
-        }
-
-        @Nullable
-        private IInlineSuggestionsResponseCallback getResponseCallback() {
-            try {
-                return mResponseCallback.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException e) {
-                Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
-            } catch (CancellationException e) {
-                Log.w(TAG, "Inline suggestions callback cancelled");
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-            return null;
-        }
-    }
-
     /**
      * Reads a new structure and then request a new fill response from the fill service.
      */
@@ -767,7 +690,6 @@
         mFlags = flags;
         this.taskId = taskId;
         this.uid = uid;
-        this.userId = userId;
         mStartTime = SystemClock.elapsedRealtime();
         mService = service;
         mLock = lock;
@@ -786,7 +708,8 @@
         mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
         setClientLocked(client);
 
-        mInputMethodManagerInternal = inputMethodManagerInternal;
+        mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
+                componentName);
 
         mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -2679,7 +2602,7 @@
 
         if (response.supportsInlineSuggestions()) {
             synchronized (mLock) {
-                if (requestShowInlineSuggestions(response, mInlineSuggestionsRequestCallback)) {
+                if (requestShowInlineSuggestionsLocked(response)) {
                     //TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
                     // rather than here where framework sends back the response.
                     mService.logDatasetShown(id, mClientState);
@@ -2722,21 +2645,21 @@
     /**
      * Returns whether we made a request to show inline suggestions.
      */
-    private boolean requestShowInlineSuggestions(@NonNull FillResponse response,
-            @Nullable InlineSuggestionsRequestCallbackImpl callback) {
+    private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response) {
         final List<Dataset> datasets = response.getDatasets();
         if (datasets == null) {
             Log.w(TAG, "response returned null datasets");
             return false;
         }
 
-        if (callback == null || callback.getRequest() == null
-                || callback.getResponseCallback() == null) {
+        final InlineSuggestionSession.ImeResponse imeResponse =
+                mInlineSuggestionSession.waitAndGetImeResponse();
+        if (imeResponse == null) {
             Log.w(TAG, "Session input method callback is not set yet");
             return false;
         }
 
-        final InlineSuggestionsRequest request = callback.getRequest();
+        final InlineSuggestionsRequest request = imeResponse.getRequest();
         InlineSuggestionsResponse inlineSuggestionsResponse =
                 InlineSuggestionFactory.createInlineSuggestionsResponse(request,
                         response.getRequestId(),
@@ -2747,7 +2670,7 @@
                             }
                         });
         try {
-            callback.getResponseCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
+            imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
         } catch (RemoteException e) {
             Log.w(TAG, "onFillReady() remote error calling onInlineSuggestionsResponse()");
             return false;
@@ -3029,12 +2952,21 @@
 
         final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
 
+        // There are 3 cases when augmented autofill should ask IME for a new request:
+        // 1. standard autofill provider is None
+        // 2. standard autofill provider doesn't support inline (and returns null response)
+        // 3. standard autofill provider supports inline, but isn't called because the field
+        // doesn't want autofill
+        if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabled()) {
+            if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
+            mInlineSuggestionSession.createRequest(mCurrentViewId);
+        }
+        InlineSuggestionSession.ImeResponse imeResponse =
+                mInlineSuggestionSession.waitAndGetImeResponse();
         final InlineSuggestionsRequest inlineSuggestionsRequest =
-                mInlineSuggestionsRequestCallback != null
-                        ? mInlineSuggestionsRequestCallback.getRequest() : null;
+                imeResponse != null ? imeResponse.getRequest() : null;
         final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
-                mInlineSuggestionsRequestCallback != null
-                        ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
+                imeResponse != null ? imeResponse.getCallback() : null;
         remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
                 currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
                     synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
index 8d476d7..e813dae 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
@@ -48,6 +48,7 @@
         super(context);
         mOnErrorCallback = onErrorCallback;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        setFocusable(false);
     }
 
     @Override
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index bdcd832..63d0924 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -396,6 +396,7 @@
      * @param origIntent The original intent that triggered ephemeral resolution
      * @param resolvedType The resolved type of the intent
      * @param callingPkg The app requesting the ephemeral application
+     * @param callingFeatureId The feature in the package
      * @param isRequesterInstantApp Whether or not the app requesting the ephemeral application
      *                              is an instant app
      * @param verificationBundle Optional bundle to pass to the installer for additional
@@ -404,7 +405,8 @@
      */
     public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPkg,
-            boolean isRequesterInstantApp, Bundle verificationBundle, int userId);
+            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+            Bundle verificationBundle, int userId);
 
     /**
      * Grants implicit access based on an interaction between two apps. This grants the target app
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index cbf6c27..0e5a6bb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -284,17 +284,17 @@
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
                         | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
+
+    static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
+                PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+                        | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+                        | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+                        | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
                         | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
                         | PhoneStateListener.LISTEN_BARRING_INFO;
 
-    static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
-                | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
-                | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
-                | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES;
-
     static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
                     | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
@@ -2535,7 +2535,7 @@
             }
         }
 
-        if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
+        if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
             // check if calling app has either permission READ_PRECISE_PHONE_STATE
             // or with carrier privileges
             try {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b994e6c..0eedf8a 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1261,8 +1261,9 @@
             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
                 try {
                     int result = ActivityTaskManager.getService().startActivityWithConfig(
-                            null, getContext().getBasePackageName(), homeIntent, null, null, null,
-                            0, 0, mConfiguration, null, UserHandle.USER_CURRENT);
+                            null, getContext().getBasePackageName(), getContext().getFeatureId(),
+                            homeIntent, null, null, null, 0, 0, mConfiguration, null,
+                            UserHandle.USER_CURRENT);
                     if (ActivityManager.isStartResultSuccessful(result)) {
                         dockAppStarted = true;
                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 982466d..97b5eaa 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -417,22 +417,23 @@
     }
 
     private boolean appRestrictedAnyInBackground(final int uid, final String packageName) {
-        final int mode = mAm.mAppOpsService.checkOperation(
+        final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                 AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName);
         return (mode != AppOpsManager.MODE_ALLOWED);
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
-            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
+            @Nullable String callingFeatureId, final int userId)
             throws TransactionTooLargeException {
         return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
-                callingPackage, userId, false);
+                callingPackage, callingFeatureId, userId, false);
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
             int callingPid, int callingUid, boolean fgRequired, String callingPackage,
-            final int userId, boolean allowBackgroundActivityStarts)
-            throws TransactionTooLargeException {
+            @Nullable String callingFeatureId, final int userId,
+            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
 
@@ -488,7 +489,7 @@
         // If this is a direct-to-foreground start, make sure it is allowed as per the app op.
         boolean forceSilentAbort = false;
         if (fgRequired) {
-            final int mode = mAm.mAppOpsService.checkOperation(
+            final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
             switch (mode) {
                 case AppOpsManager.MODE_ALLOWED:
@@ -566,7 +567,7 @@
         // review is completed.
 
         // XXX This is not dealing with fgRequired!
-        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
+        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingFeatureId,
                 callingUid, service, callerFg, userId)) {
             return null;
         }
@@ -673,8 +674,8 @@
     }
 
     private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
-            String callingPackage, int callingUid, Intent service, boolean callerFg,
-            final int userId) {
+            String callingPackage, @Nullable String callingFeatureId, int callingUid,
+            Intent service, boolean callerFg, final int userId) {
         if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                 r.packageName, r.userId)) {
 
@@ -686,7 +687,7 @@
             }
 
             IIntentSender target = mAm.mPendingIntentController.getIntentSender(
-                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
+                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage, callingFeatureId,
                     callingUid, userId, null, null, 0, new Intent[]{service},
                     new String[]{service.resolveType(mAm.mContext.getContentResolver())},
                     PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
@@ -1287,7 +1288,7 @@
             }
             // Instant apps need permission to create foreground services.
             if (r.appInfo.isInstantApp()) {
-                final int mode = mAm.mAppOpsService.checkOperation(
+                final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                         AppOpsManager.OP_INSTANT_APP_START_FOREGROUND,
                         r.appInfo.uid,
                         r.appInfo.packageName);
@@ -1354,7 +1355,7 @@
 
             try {
                 boolean ignoreForeground = false;
-                final int mode = mAm.mAppOpsService.checkOperation(
+                final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                 switch (mode) {
                     case AppOpsManager.MODE_ALLOWED:
@@ -2289,7 +2290,7 @@
                 return new ServiceLookupResult(null, r.permission);
             } else if (r.permission != null && callingPackage != null) {
                 final int opCode = AppOpsManager.permissionToOpCode(r.permission);
-                if (opCode != AppOpsManager.OP_NONE && mAm.mAppOpsService.checkOperation(
+                if (opCode != AppOpsManager.OP_NONE && mAm.getAppOpsManager().checkOpNoThrow(
                         opCode, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: Accessing service " + r.shortInstanceName
                             + " from pid=" + callingPid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3ad96ea..d5a7253 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -466,18 +466,9 @@
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
-    // How long we wait for an attached process to publish its content providers
-    // before we decide it must be hung.
-    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
-
     // How long we wait to kill an application zygote, after the last process using
     // it has gone away.
     static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
-    /**
-     * How long we wait for an provider to be published. Should be longer than
-     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
-     */
-    static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
@@ -1240,6 +1231,7 @@
      * Information about and control over application operations
      */
     final AppOpsService mAppOpsService;
+    private AppOpsManager mAppOpsManager;
 
     /**
      * List of initialization arguments to pass to all processes when binding applications to them.
@@ -2109,7 +2101,7 @@
                 new IAppOpsCallback.Stub() {
                     @Override public void opChanged(int op, int uid, String packageName) {
                         if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
-                            if (mAppOpsService.checkOperation(op, uid, packageName)
+                            if (getAppOpsManager().checkOpNoThrow(op, uid, packageName)
                                     != AppOpsManager.MODE_ALLOWED) {
                                 runInBackgroundDisabled(uid);
                             }
@@ -2400,6 +2392,13 @@
         }
     }
 
+    AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+        }
+        return mAppOpsManager;
+    }
+
     /**
      * Provides the basic functionality for activity task related tests when a handler thread is
      * given to initialize the dependency members.
@@ -2908,7 +2907,7 @@
     @Override
     public void batterySendBroadcast(Intent intent) {
         synchronized (this) {
-            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+            broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                     OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
                     Binder.getCallingPid(), UserHandle.USER_ALL);
         }
@@ -3512,30 +3511,56 @@
 
     }
 
+    /**
+     * @deprecated use {@link #startActivityWithFeature} instead
+     */
+    @Deprecated
     @Override
     public int startActivity(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
-        return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,
-                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
+        return mActivityTaskManager.startActivity(caller, callingPackage, null, intent,
+                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
     }
 
     @Override
+    public int startActivityWithFeature(IApplicationThread caller, String callingPackage,
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions) {
+        return mActivityTaskManager.startActivity(caller, callingPackage, callingFeatureId, intent,
+                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
+    }
+
+    /**
+     * @deprecated use {@link #startActivityAsUserWithFeature} instead
+     */
+    @Deprecated
+    @Override
     public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return startActivityAsUserWithFeature(caller, callingPackage, null, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId);
+    }
 
-            return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
-                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
-                    bOptions, userId);
+    @Override
+    public final int startActivityAsUserWithFeature(IApplicationThread caller,
+            String callingPackage, String callingFeatureId, Intent intent, String resolvedType,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return mActivityTaskManager.startActivityAsUser(caller, callingPackage,
+                    callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode,
+                    startFlags, profilerInfo, bOptions, userId);
     }
 
     WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-            return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent,
-                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
-                    bOptions, userId);
+            @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+            return mActivityTaskManager.startActivityAndWait(caller, callingPackage,
+                    callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode,
+                    startFlags, profilerInfo, bOptions, userId);
     }
 
     @Override
@@ -4106,12 +4131,12 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
                     if (isInstantApp) {
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
-                                null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, false,
-                                false, resolvedUserId, false);
+                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
+                                null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
+                                false, false, resolvedUserId, false);
                     } else {
-                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
-                                null, 0, null, null, null, null, false, false, resolvedUserId,
+                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
+                                null, null, 0, null, null, null, null, false, false, resolvedUserId,
                                 false);
                     }
 
@@ -4559,7 +4584,7 @@
         }
         intent.putExtra(Intent.EXTRA_UID, uid);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
-        broadcastIntentLocked(null, null, intent,
+        broadcastIntentLocked(null, null, null, intent,
                 null, null, 0, null, null, null, OP_NONE,
                 null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
                 Binder.getCallingPid(), UserHandle.getUserId(uid));
@@ -4934,7 +4959,8 @@
         if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
             Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
             msg.obj = app;
-            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
+            mHandler.sendMessageDelayed(msg,
+                    ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
         }
 
         checkTime(startTime, "attachApplicationLocked: before bindApplication");
@@ -5416,12 +5442,23 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link #getIntentSenderWithFeature} instead
+     */
+    @Deprecated
     @Override
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes,
             int flags, Bundle bOptions, int userId) {
+        return getIntentSenderWithFeature(type, packageName, null, token, resultWho, requestCode,
+                intents, resolvedTypes, flags, bOptions, userId);
+    }
 
+    @Override
+    public IIntentSender getIntentSenderWithFeature(int type, String packageName, String featureId,
+            IBinder token, String resultWho, int requestCode, Intent[] intents,
+            String[] resolvedTypes, int flags, Bundle bOptions, int userId) {
         // NOTE: The service lock isn't held in this method because nothing in the method requires
         // the service lock to be held.
 
@@ -5483,12 +5520,13 @@
             }
 
             if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-                return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
-                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+                return mAtmInternal.getIntentSender(type, packageName, featureId, callingUid,
+                        userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                        bOptions);
             }
-            return mPendingIntentController.getIntentSender(type, packageName, callingUid,
-                    userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                    bOptions);
+            return mPendingIntentController.getIntentSender(type, packageName, featureId,
+                    callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes,
+                    flags, bOptions);
         } catch (RemoteException e) {
             throw new SecurityException(e);
         }
@@ -6054,8 +6092,8 @@
             return ActivityManager.APP_START_MODE_DELAYED;
         }
         // Not in the RESTRICTED bucket so policy is based on AppOp check.
-        int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
-                uid, packageName, null, false, "");
+        int appop = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND,
+                uid, packageName, null, "");
         if (DEBUG_BACKGROUND_CHECK) {
             Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
         }
@@ -7201,7 +7239,8 @@
         }
 
         // Wait for the provider to be published...
-        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
+        final long timeout =
+                SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
         boolean timedOut = false;
         synchronized (cpr) {
             while (cpr.provider == null) {
@@ -7238,12 +7277,14 @@
             }
         }
         if (timedOut) {
-            // Note we do it afer releasing the lock.
+            // Note we do it after releasing the lock.
             String callerName = "unknown";
-            synchronized (this) {
-                final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
-                if (record != null) {
-                    callerName = record.processName;
+            if (caller != null) {
+                synchronized (this) {
+                    final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
+                    if (record != null) {
+                        callerName = record.processName;
+                    }
                 }
             }
 
@@ -7961,7 +8002,7 @@
     }
 
     boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
-        final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+        final int mode = getAppOpsManager().checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                 uid, packageName);
         return mode != AppOpsManager.MODE_ALLOWED;
     }
@@ -9439,14 +9480,14 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                             | Intent.FLAG_RECEIVER_FOREGROUND);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, intent,
+                    broadcastIntentLocked(null, null, null, intent,
                             null, null, 0, null, null, null, OP_NONE,
                             null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
                             currentUserId);
                     intent = new Intent(Intent.ACTION_USER_STARTING);
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, intent, null,
+                    broadcastIntentLocked(null, null, null, intent, null,
                             new IIntentReceiver.Stub() {
                                 @Override
                                 public void performReceive(Intent intent, int resultCode,
@@ -14585,7 +14626,8 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, boolean requireForeground, String callingPackage, int userId)
+            String resolvedType, boolean requireForeground, String callingPackage,
+            String callingFeatureId, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -14607,7 +14649,7 @@
             try {
                 res = mServices.startServiceLocked(caller, service,
                         resolvedType, callingPid, callingUid,
-                        requireForeground, callingPackage, userId);
+                        requireForeground, callingPackage, callingFeatureId, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -15085,9 +15127,20 @@
         return didSomething;
     }
 
+    /**
+     * @deprecated Use {@link #registerReceiverWithFeature}
+     */
+    @Deprecated
     public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
             int flags) {
+        return registerReceiverWithFeature(caller, callerPackage, null, receiver, filter,
+                permission, userId, flags);
+    }
+
+    public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
+            String callerFeatureId, IIntentReceiver receiver, IntentFilter filter,
+            String permission, int userId, int flags) {
         enforceNotIsolatedCaller("registerReceiver");
         ArrayList<Intent> stickyIntents = null;
         ProcessRecord callerApp = null;
@@ -15223,7 +15276,7 @@
                         + " was previously registered for user " + rl.userId
                         + " callerPackage is " + callerPackage);
             }
-            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
+            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                     permission, callingUid, userId, instantApp, visibleToInstantApps);
             if (rl.containsFilter(filter)) {
                 Slog.w(TAG, "Receiver with filter " + filter
@@ -15248,7 +15301,7 @@
                     Intent intent = allSticky.get(i);
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
-                            null, -1, -1, false, null, null, OP_NONE, null, receivers,
+                            null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
                             null, 0, null, null, false, true, true, -1, false,
                             false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                     queue.enqueueParallelBroadcastLocked(r);
@@ -15478,20 +15531,20 @@
 
     @GuardedBy("this")
     final int broadcastIntentLocked(ProcessRecord callerApp,
-            String callerPackage, Intent intent, String resolvedType,
+            String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
             int realCallingPid, int userId) {
-        return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
-            resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
-            sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
-            false /* allowBackgroundActivityStarts */);
+        return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
+                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
+                appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
+                realCallingPid, userId, false /* allowBackgroundActivityStarts */);
     }
 
     @GuardedBy("this")
-    final int broadcastIntentLocked(ProcessRecord callerApp,
-            String callerPackage, Intent intent, String resolvedType,
+    final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
+            @Nullable String callerFeatureId, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
@@ -16028,8 +16081,8 @@
                         isProtectedBroadcast, registeredReceivers);
             }
             final BroadcastQueue queue = broadcastQueueForIntent(intent);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
-                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                     resultCode, resultData, resultExtras, ordered, sticky, false, userId,
                     allowBackgroundActivityStarts, timeoutExempt);
@@ -16125,8 +16178,8 @@
         if ((receivers != null && receivers.size() > 0)
                 || resultTo != null) {
             BroadcastQueue queue = broadcastQueueForIntent(intent);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
-                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                     resultData, resultExtras, ordered, sticky, false, userId,
                     allowBackgroundActivityStarts, timeoutExempt);
@@ -16245,11 +16298,25 @@
         return intent;
     }
 
+    /**
+     * @deprecated Use {@link #broadcastIntentWithFeature}
+     */
+    @Deprecated
     public final int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle resultExtras,
             String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean serialized, boolean sticky, int userId) {
+        return broadcastIntentWithFeature(caller, null, intent, resolvedType, resultTo, resultCode,
+                resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky,
+                userId);
+    }
+
+    public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
+            Intent intent, String resolvedType, IIntentReceiver resultTo,
+            int resultCode, String resultData, Bundle resultExtras,
+            String[] requiredPermissions, int appOp, Bundle bOptions,
+            boolean serialized, boolean sticky, int userId) {
         enforceNotIsolatedCaller("broadcastIntent");
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
@@ -16261,7 +16328,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 return broadcastIntentLocked(callerApp,
-                        callerApp != null ? callerApp.info.packageName : null,
+                        callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                         intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                         requiredPermissions, appOp, bOptions, serialized, sticky,
                         callingPid, callingUid, callingUid, callingPid, userId);
@@ -16271,9 +16338,9 @@
         }
     }
 
-    int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
-            int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle resultExtras,
+    int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
+            int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+            IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
             int userId, boolean allowBackgroundActivityStarts) {
         synchronized(this) {
@@ -16283,11 +16350,10 @@
             String[] requiredPermissions = requiredPermission == null ? null
                     : new String[] {requiredPermission};
             try {
-                return broadcastIntentLocked(null, packageName, intent, resolvedType,
-                        resultTo, resultCode, resultData, resultExtras,
-                        requiredPermissions, OP_NONE, bOptions, serialized,
-                        sticky, -1, uid, realCallingUid, realCallingPid, userId,
-                        allowBackgroundActivityStarts);
+                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
+                        resultTo, resultCode, resultData, resultExtras, requiredPermissions,
+                        OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
+                        realCallingPid, userId, allowBackgroundActivityStarts);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -18891,23 +18957,24 @@
         }
 
         @Override
-        public int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
-                int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
-                int resultCode, String resultData, Bundle resultExtras, String requiredPermission,
-                Bundle bOptions, boolean serialized, boolean sticky, int userId,
-                boolean allowBackgroundActivityStarts) {
+        public int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
+                int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+                IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
+                String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
+                int userId, boolean allowBackgroundActivityStarts) {
             synchronized (ActivityManagerService.this) {
-                return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
-                        realCallingUid, realCallingPid, intent, resolvedType, resultTo, resultCode,
-                        resultData, resultExtras, requiredPermission, bOptions, serialized, sticky,
-                        userId, allowBackgroundActivityStarts);
+                return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
+                        uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
+                        resultCode, resultData, resultExtras, requiredPermission, bOptions,
+                        serialized, sticky, userId, allowBackgroundActivityStarts);
             }
         }
 
         @Override
         public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
-                boolean fgRequired, String callingPackage, int userId,
-                boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
+                boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
+                int userId, boolean allowBackgroundActivityStarts)
+                throws TransactionTooLargeException {
             synchronized(ActivityManagerService.this) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                         "startServiceInPackage: " + service + " type=" + resolvedType);
@@ -18915,8 +18982,8 @@
                 ComponentName res;
                 try {
                     res = mServices.startServiceLocked(null, service,
-                            resolvedType, -1, uid, fgRequired, callingPackage, userId,
-                            allowBackgroundActivityStarts);
+                            resolvedType, -1, uid, fgRequired, callingPackage,
+                            callingFeatureId, userId, allowBackgroundActivityStarts);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
@@ -19009,7 +19076,7 @@
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING
                         | Intent.FLAG_RECEIVER_FOREGROUND
                         | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                         OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
                         Binder.getCallingPid(), UserHandle.USER_ALL);
                 if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
@@ -19020,7 +19087,7 @@
                     if (initLocale || !mProcessesReady) {
                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     }
-                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                             OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
                             Binder.getCallingPid(), UserHandle.USER_ALL);
                 }
@@ -19035,7 +19102,7 @@
                     // Typically only app stores will have this permission.
                     String[] permissions =
                             new String[] { android.Manifest.permission.INSTALL_PACKAGES };
-                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
+                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null,
                             permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                             Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
                 }
@@ -19060,7 +19127,7 @@
                     intent.putExtra("reason", reason);
                 }
 
-                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                         OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
                         Binder.getCallingPid(), UserHandle.USER_ALL);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 53a967b..c0e98cd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -534,13 +534,13 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, intent, mimeType,
-                        null, null, 0, mStartFlags, profilerInfo,
+                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
+                        mimeType, null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mInternal.startActivityAsUser(null, SHELL_PACKAGE_NAME, intent, mimeType,
-                        null, null, 0, mStartFlags, profilerInfo,
+                res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null,
+                        intent, mimeType, null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
             final long endTime = SystemClock.uptimeMillis();
@@ -652,7 +652,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                asForeground, SHELL_PACKAGE_NAME, mUserId);
+                asForeground, SHELL_PACKAGE_NAME, null, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
@@ -742,8 +742,9 @@
         pw.println("Broadcasting: " + intent);
         pw.flush();
         Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle();
-        mInterface.broadcastIntent(null, intent, null, receiver, 0, null, null, requiredPermissions,
-                android.app.AppOpsManager.OP_NONE, bundle, true, false, mUserId);
+        mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
+                requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false,
+                mUserId);
         receiver.waitForFinish();
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 1ec8db0..578db6f 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -27,6 +27,7 @@
     // Back-pointer to the list this filter is in.
     final ReceiverList receiverList;
     final String packageName;
+    final String featureId;
     final String requiredPermission;
     final int owningUid;
     final int owningUserId;
@@ -34,11 +35,12 @@
     final boolean visibleToInstantApp;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
-            String _packageName, String _requiredPermission, int _owningUid, int _userId,
+            String _packageName, String _featureId, String _requiredPermission, int _owningUid, int _userId,
             boolean _instantApp, boolean _visibleToInstantApp) {
         super(_filter);
         receiverList = _receiverList;
         packageName = _packageName;
+        featureId = _featureId;
         requiredPermission = _requiredPermission;
         owningUid = _owningUid;
         owningUserId = _userId;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6697b5a..26ef707 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -648,10 +648,10 @@
                 skip = true;
             } else {
                 final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
-                // TODO moltmann: Set featureId from caller
                 if (opCode != AppOpsManager.OP_NONE
-                        && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
-                        r.callerPackage, null, false, "") != AppOpsManager.MODE_ALLOWED) {
+                        && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
+                        r.callerPackage, r.callerFeatureId, "")
+                        != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: broadcasting "
                             + r.intent.toString()
                             + " from " + r.callerPackage + " (pid="
@@ -681,10 +681,9 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.mAppOpsService.noteOperation(appOp,
-                        filter.receiverList.uid, filter.packageName, null, false, "")
+                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
+                        filter.receiverList.uid, filter.packageName, filter.featureId, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent.toString()
@@ -714,10 +713,9 @@
                 skip = true;
             }
         }
-        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.mAppOpsService.noteOperation(r.appOp,
-                filter.receiverList.uid, filter.packageName, null, false, "")
+                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
+                filter.receiverList.uid, filter.packageName, filter.featureId, "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent.toString()
@@ -863,7 +861,8 @@
         if (callerForeground && receiverRecord.intent.getComponent() != null) {
             IIntentSender target = mService.mPendingIntentController.getIntentSender(
                     ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
-                    receiverRecord.callingUid, receiverRecord.userId, null, null, 0,
+                    receiverRecord.callerFeatureId, receiverRecord.callingUid,
+                    receiverRecord.userId, null, null, 0,
                     new Intent[]{receiverRecord.intent},
                     new String[]{receiverRecord.intent.resolveType(mService.mContext
                             .getContentResolver())},
@@ -1371,10 +1370,9 @@
             skip = true;
         } else if (!skip && info.activityInfo.permission != null) {
             final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
-            // TODO moltmann: Set featureId from caller
             if (opCode != AppOpsManager.OP_NONE
-                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage,
-                    null, false, "") != AppOpsManager.MODE_ALLOWED) {
+                    && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid, r.callerPackage,
+                    r.callerFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
                 Slog.w(TAG, "Appop Denial: broadcasting "
                         + r.intent.toString()
                         + " from " + r.callerPackage + " (pid="
@@ -1410,11 +1408,10 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.mAppOpsService.noteOperation(appOp,
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null,
-                        false, "")
+                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+                        null /* default featureId */, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent + " to "
@@ -1428,11 +1425,10 @@
                 }
             }
         }
-        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.mAppOpsService.noteOperation(r.appOp,
-                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null, false,
-                "")
+                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
+                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+                null  /* default featureId */, "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent + " to "
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index f263886..8ef67f9 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.ComponentName;
@@ -51,6 +52,7 @@
     final ComponentName targetComp; // original component name set on the intent
     final ProcessRecord callerApp; // process that sent this
     final String callerPackage; // who sent this
+    final @Nullable String callerFeatureId; // which feature in the package sent this
     final int callingPid;   // the pid of who sent this
     final int callingUid;   // the uid of who sent this
     final boolean callerInstantApp; // caller is an Instant App?
@@ -233,7 +235,8 @@
 
     BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
-            int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType,
+            @Nullable String _callerFeatureId, int _callingPid, int _callingUid,
+            boolean _callerInstantApp, String _resolvedType,
             String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
             IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
             boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
@@ -246,6 +249,7 @@
         targetComp = _intent.getComponent();
         callerApp = _callerApp;
         callerPackage = _callerPackage;
+        callerFeatureId = _callerFeatureId;
         callingPid = _callingPid;
         callingUid = _callingUid;
         callerInstantApp = _callerInstantApp;
@@ -280,6 +284,7 @@
 
         callerApp = from.callerApp;
         callerPackage = from.callerPackage;
+        callerFeatureId = from.callerFeatureId;
         callingPid = from.callingPid;
         callingUid = from.callingUid;
         callerInstantApp = from.callerInstantApp;
@@ -343,8 +348,8 @@
         }
 
         // build a new BroadcastRecord around that single-target list
-        BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp,
-                callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+        BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                 requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
                 resultData, resultExtras, ordered, sticky, initialSticky, userId,
                 allowBackgroundActivityStarts, timeoutExempt);
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index df76713..eacf088 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -23,6 +23,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
@@ -88,9 +89,9 @@
         }
     }
 
-    public PendingIntentRecord getIntentSender(int type, String packageName, int callingUid,
-            int userId, IBinder token, String resultWho, int requestCode, Intent[] intents,
-            String[] resolvedTypes, int flags, Bundle bOptions) {
+    public PendingIntentRecord getIntentSender(int type, String packageName,
+            @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho,
+            int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions) {
         synchronized (mLock) {
             if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
 
@@ -109,8 +110,8 @@
             flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
                     | PendingIntent.FLAG_UPDATE_CURRENT);
 
-            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, token,
-                    resultWho, requestCode, intents, resolvedTypes, flags,
+            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
+                    token, resultWho, requestCode, intents, resolvedTypes, flags,
                     SafeActivityOptions.fromBundle(bOptions), userId);
             WeakReference<PendingIntentRecord> ref;
             ref = mIntentSenderRecords.get(key);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3ba2210..d54d2d7 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -17,14 +17,16 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.START_SUCCESS;
+
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.content.IIntentSender;
-import android.content.IIntentReceiver;
 import android.app.PendingIntent;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Bundle;
@@ -72,6 +74,7 @@
     final static class Key {
         final int type;
         final String packageName;
+        final String featureId;
         final IBinder activity;
         final String who;
         final int requestCode;
@@ -86,10 +89,11 @@
 
         private static final int ODD_PRIME_NUMBER = 37;
 
-        Key(int _t, String _p, IBinder _a, String _w,
+        Key(int _t, String _p, @Nullable String _featureId, IBinder _a, String _w,
                 int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) {
             type = _t;
             packageName = _p;
+            featureId = _featureId;
             activity = _a;
             who = _w;
             requestCode = _r;
@@ -140,6 +144,9 @@
                 if (!Objects.equals(packageName, other.packageName)) {
                     return false;
                 }
+                if (!Objects.equals(featureId, other.featureId)) {
+                    return false;
+                }
                 if (activity != other.activity) {
                     return false;
                 }
@@ -175,7 +182,8 @@
         }
 
         public String toString() {
-            return "Key{" + typeName() + " pkg=" + packageName
+            return "Key{" + typeName()
+                + " pkg=" + packageName + (featureId != null ? "/" + featureId : "")
                 + " intent="
                 + (requestIntent != null
                         ? requestIntent.toShortString(false, true, false, false) : "<null>")
@@ -403,19 +411,20 @@
 
                         if (key.allIntents != null && key.allIntents.length > 1) {
                             res = controller.mAtmInternal.startActivitiesInPackage(
-                                    uid, callingPid, callingUid, key.packageName, allIntents,
-                                    allResolvedTypes, resultTo, mergedOptions, userId,
+                                    uid, callingPid, callingUid, key.packageName, key.featureId,
+                                    allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
                                     mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         } else {
-                            res = controller.mAtmInternal.startActivityInPackage(
-                                    uid, callingPid, callingUid, key.packageName, finalIntent,
+                            res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
+                                    callingUid, key.packageName, key.featureId, finalIntent,
                                     resolvedType, resultTo, resultWho, requestCode, 0,
                                     mergedOptions, userId, null, "PendingIntentRecord",
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
-                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
+                                    mAllowBgActivityStartsForActivitySender.contains(
+                                            whitelistToken));
                         }
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startActivity intent", e);
@@ -430,11 +439,12 @@
                         // If a completion callback has been requested, require
                         // that the broadcast be delivered synchronously
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
-                                uid, callingUid, callingPid, finalIntent, resolvedType,
-                                finishedReceiver, code, null, null, requiredPermission, options,
-                                (finishedReceiver != null), false, userId,
+                                key.featureId, uid, callingUid, callingPid, finalIntent,
+                                resolvedType, finishedReceiver, code, null, null,
+                                requiredPermission, options, (finishedReceiver != null), false,
+                                userId,
                                 mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
-                                || allowTrampoline);
+                                        || allowTrampoline);
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
                         }
@@ -447,7 +457,7 @@
                     try {
                         controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
                                 key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
-                                key.packageName, userId,
+                                key.packageName, key.featureId, userId,
                                 mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
                                 || allowTrampoline);
                     } catch (RuntimeException e) {
@@ -496,6 +506,7 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("uid="); pw.print(uid);
                 pw.print(" packageName="); pw.print(key.packageName);
+                pw.print(" featureId="); pw.print(key.featureId);
                 pw.print(" type="); pw.print(key.typeName());
                 pw.print(" flags=0x"); pw.println(Integer.toHexString(key.flags));
         if (key.activity != null || key.who != null) {
@@ -545,6 +556,10 @@
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(' ');
         sb.append(key.packageName);
+        if (key.featureId != null) {
+            sb.append('/');
+            sb.append(key.featureId);
+        }
         sb.append(' ');
         sb.append(key.typeName());
         if (whitelistDuration != null) {
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index beb0e47..747e8a8 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -108,8 +108,8 @@
 
         mIntent.setComponent(componentName);
         synchronized (mService) {
-            mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
-                    AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+            mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null,
+                    null, AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
                     Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
         }
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c75ee04..e575e10 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2447,10 +2447,10 @@
                 int realCallingPid, @UserIdInt int userId) {
             // TODO b/64165549 Verify that mLock is not held before calling AMS methods
             synchronized (mService) {
-                return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
-                        resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
-                        ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid,
-                        userId);
+                return mService.broadcastIntentLocked(null, null, null, intent, resolvedType,
+                        resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp,
+                        bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
+                        realCallingPid, userId);
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index e8cb163..061cbd8 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -170,6 +170,11 @@
         mFileWriteHandler.post(rpr);
     }
 
+    public void deleteNotificationHistoryItem(String pkg, long postedTime) {
+        RemoveNotificationRunnable rnr = new RemoveNotificationRunnable(pkg, postedTime);
+        mFileWriteHandler.post(rnr);
+    }
+
     public void addNotification(final HistoricalNotification notification) {
         synchronized (mLock) {
             mBuffer.addNewNotificationToWrite(notification);
@@ -367,6 +372,49 @@
         }
     }
 
+    final class RemoveNotificationRunnable implements Runnable {
+        private String mPkg;
+        private long mPostedTime;
+        private NotificationHistory mNotificationHistory;
+
+        public RemoveNotificationRunnable(String pkg, long postedTime) {
+            mPkg = pkg;
+            mPostedTime = postedTime;
+        }
+
+        @VisibleForTesting
+        void setNotificationHistory(NotificationHistory nh) {
+            mNotificationHistory = nh;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "RemovePackageRunnable");
+            synchronized (mLock) {
+                // Remove from pending history
+                mBuffer.removeNotificationFromWrite(mPkg, mPostedTime);
+
+                Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
+                while (historyFileItr.hasNext()) {
+                    final AtomicFile af = historyFileItr.next();
+                    try {
+                        NotificationHistory notificationHistory = mNotificationHistory != null
+                                ? mNotificationHistory
+                                : new NotificationHistory();
+                        readLocked(af, notificationHistory,
+                                new NotificationHistoryFilter.Builder().build());
+                        if(notificationHistory.removeNotificationFromWrite(mPkg, mPostedTime)) {
+                            writeLocked(af, notificationHistory);
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Cannot clean up file on notification removal "
+                                + af.getBaseFile().getName(), e);
+                    }
+                }
+            }
+        }
+    }
+
     public static final class NotificationHistoryFileAttrProvider implements
             NotificationHistoryDatabase.FileAttrProvider {
         final static String TAG = "NotifHistoryFileDate";
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 41bc29f..9aab0fd 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -130,7 +130,7 @@
         }
     }
 
-    public void onPackageRemoved(int userId, String packageName) {
+    public void onPackageRemoved(@UserIdInt int userId, String packageName) {
         synchronized (mLock) {
             if (!mUserUnlockedStates.get(userId, false)) {
                 if (mHistoryEnabled.get(userId, false)) {
@@ -150,6 +150,22 @@
         }
     }
 
+    public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
+        synchronized (mLock) {
+            int userId = UserHandle.getUserId(uid);
+            final NotificationHistoryDatabase userHistory =
+                    getUserHistoryAndInitializeIfNeededLocked(userId);
+            // TODO: it shouldn't be possible to delete a notification entry while the user is
+            // locked but we should handle it
+            if (userHistory == null) {
+                Slog.w(TAG, "Attempted to remove notif for locked/gone/disabled user "
+                        + userId);
+                return;
+            }
+            userHistory.deleteNotificationHistoryItem(pkg, postedTime);
+        }
+    }
+
     // TODO: wire this up to AMS when power button is long pressed
     public void triggerWriteToDisk() {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 46dc21bb..ea77c36 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -193,6 +193,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.IStatusBarNotificationHolder;
@@ -2780,7 +2781,7 @@
                         if (!isSystemToast) {
                             int count = 0;
                             final int N = mToastQueue.size();
-                            for (int i=0; i<N; i++) {
+                            for (int i = 0; i < N; i++) {
                                 final ToastRecord r = mToastQueue.get(i);
                                 if (r.pkg.equals(pkg)) {
                                     count++;
@@ -2820,7 +2821,7 @@
 
             if (pkg == null || token == null) {
                 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " token=" + token);
-                return ;
+                return;
             }
 
             synchronized (mToastQueue) {
@@ -2933,14 +2934,14 @@
 
         /**
          * Updates the enabled state for notifications for the given package (and uid).
-         * Additionally, this method marks the app importance as locked by the user, which means
+         * Additionally, this method marks the app importance as locked by the user, which
+         * means
          * that notifications from the app will <b>not</b> be considered for showing a
          * blocking helper.
          *
-         * @param pkg package that owns the notifications to update
-         * @param uid uid of the app providing notifications
+         * @param pkg     package that owns the notifications to update
+         * @param uid     uid of the app providing notifications
          * @param enabled whether notifications should be enabled for the app
-         *
          * @see #setNotificationsEnabledForPackage(String, int, boolean)
          */
         @Override
@@ -3031,6 +3032,12 @@
         }
 
         @Override
+        public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
+            checkCallerIsSystem();
+            mHistoryManager.deleteNotificationHistoryItem(pkg, uid, postedTime);
+        }
+
+        @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
             return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -3215,9 +3222,10 @@
 
         @Override
         public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
-                String channelId, boolean includeDeleted) {
+                String channelId, String conversationId, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mPreferencesHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
+            return mPreferencesHelper.getConversationNotificationChannel(
+                    pkg, uid, channelId, conversationId, true, includeDeleted);
         }
 
         @Override
@@ -3354,6 +3362,27 @@
         }
 
         @Override
+        public ParceledListSlice<ConversationChannelWrapper> getConversationsForPackage(String pkg,
+                int uid) {
+            enforceSystemOrSystemUI("getConversationsForPackage");
+            ArrayList<ConversationChannelWrapper> conversations =
+                    mPreferencesHelper.getConversations(pkg, uid);
+            for (ConversationChannelWrapper conversation : conversations) {
+                LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+                        .setPackage(pkg)
+                        .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+                        .setShortcutIds(Arrays.asList(
+                                conversation.getNotificationChannel().getConversationId()));
+                List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+                        query, UserHandle.of(UserHandle.getUserId(uid)));
+                if (shortcuts != null && !shortcuts.isEmpty()) {
+                    conversation.setShortcutInfo(shortcuts.get(0));
+                }
+            }
+            return new ParceledListSlice<>(conversations);
+        }
+
+        @Override
         public NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(
                 String pkg, int uid, String groupId, boolean includeDeleted) {
             enforceSystemOrSystemUI("getPopulatedNotificationChannelGroupForPackage");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index fe39322..a244c48 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.RankingHelperProto;
 import android.text.TextUtils;
@@ -1176,6 +1177,43 @@
         return groups;
     }
 
+    public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
+        Objects.requireNonNull(pkg);
+        synchronized (mPackagePreferences) {
+            PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+            if (r == null) {
+                return new ArrayList<>();
+            }
+            ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+            int N = r.channels.size();
+            for (int i = 0; i < N; i++) {
+                final NotificationChannel nc = r.channels.valueAt(i);
+                if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
+                    ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+                    conversation.setNotificationChannel(nc);
+                    conversation.setParentChannelLabel(
+                            r.channels.get(nc.getParentChannelId()).getName());
+                    boolean blockedByGroup = false;
+                    if (nc.getGroup() != null) {
+                        NotificationChannelGroup group = r.groups.get(nc.getGroup());
+                        if (group != null) {
+                            if (group.isBlocked()) {
+                                blockedByGroup = true;
+                            } else {
+                                conversation.setGroupLabel(group.getName());
+                            }
+                        }
+                    }
+                    if (!blockedByGroup) {
+                        conversations.add(conversation);
+                    }
+                }
+            }
+
+            return conversations;
+        }
+    }
+
     @Override
     public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
             boolean includeDeleted) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 3c31f6a..d75eb6d 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -913,9 +913,9 @@
                 }
 
                 try {
-                    ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
-                            null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
-                            userId);
+                    ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
+                            null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
+                            null, false, false, userId);
                 } catch (RemoteException e) {
                     // Intentionally left empty.
                 }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 3ad1207..8c13b5b 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -34,6 +34,7 @@
 import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
 import android.content.pm.parsing.ComponentParseUtils.ParsedService;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Process;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -171,11 +172,13 @@
         @Override
         public boolean packageIsEnabled(AndroidPackage pkg) {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "packageIsEnabled");
+            final long token = Binder.clearCallingIdentity();
             try {
                 // TODO(b/135203078): Do not use toAppInfo
                 return mInjector.getCompatibility().isChangeEnabled(
                         PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
             } finally {
+                Binder.restoreCallingIdentity(token);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
         }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 74d2efe..cd4485e 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -44,9 +44,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -54,10 +52,8 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
-import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.ArrayList;
@@ -69,7 +65,7 @@
 
     private Context mContext;
     private Injector mInjector;
-    private AppOpsService mAppOpsService;
+    private AppOpsManager mAppOpsManager;
 
     public CrossProfileAppsServiceImpl(Context context) {
         this(context, new InjectorImpl(context));
@@ -100,6 +96,7 @@
     public void startActivityAsUser(
             IApplicationThread caller,
             String callingPackage,
+            String callingFeatureId,
             ComponentName component,
             @UserIdInt int userId,
             boolean launchMainActivity) throws RemoteException {
@@ -165,7 +162,7 @@
         launchIntent.setPackage(null);
         launchIntent.setComponent(component);
         mInjector.getActivityTaskManagerInternal().startActivityAsUser(
-                caller, callingPackage, launchIntent,
+                caller, callingPackage, callingFeatureId, launchIntent,
                 launchMainActivity
                         ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
                         : null,
@@ -176,6 +173,7 @@
     public void startActivityAsUserByIntent(
             IApplicationThread caller,
             String callingPackage,
+            String callingFeatureId,
             Intent intent,
             @UserIdInt int userId) throws RemoteException {
         Objects.requireNonNull(callingPackage);
@@ -212,8 +210,8 @@
 
         verifyActivityCanHandleIntent(launchIntent, callingUid, userId);
 
-        mInjector.getActivityTaskManagerInternal().startActivityAsUser(
-                caller, callingPackage, launchIntent, /* options= */ null, userId);
+        mInjector.getActivityTaskManagerInternal().startActivityAsUser(caller, callingPackage,
+                callingFeatureId, launchIntent, /* options= */ null, userId);
     }
 
     @Override
@@ -545,12 +543,11 @@
                 permission, uid, /* owningUid= */-1, /* exported= */ true);
     }
 
-    private AppOpsService getAppOpsService() {
-        if (mAppOpsService == null) {
-            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
-            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+    private AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         }
-        return mAppOpsService;
+        return mAppOpsManager;
     }
 
     private static class InjectorImpl implements Injector {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 8333ae5..0b0f139 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -222,6 +222,7 @@
                         sanitizedIntent,
                         failureIntent,
                         requestObj.callingPackage,
+                        requestObj.callingFeatureId,
                         requestObj.verificationBundle,
                         requestObj.resolvedType,
                         requestObj.userId,
@@ -266,6 +267,7 @@
             @NonNull Intent sanitizedIntent,
             @Nullable Intent failureIntent,
             @NonNull String callingPackage,
+            @Nullable String callingFeatureId,
             @Nullable Bundle verificationBundle,
             @NonNull String resolvedType,
             int userId,
@@ -308,9 +310,10 @@
                         onFailureIntent = failureIntent;
                     }
                     final IIntentSender failureIntentTarget = ActivityManager.getService()
-                            .getIntentSender(
+                            .getIntentSenderWithFeature(
                                     ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                                    null /*token*/, null /*resultWho*/, 1 /*requestCode*/,
+                                    callingFeatureId, null /*token*/, null /*resultWho*/,
+                                    1 /*requestCode*/,
                                     new Intent[] { onFailureIntent },
                                     new String[] { resolvedType },
                                     PendingIntent.FLAG_CANCEL_CURRENT
@@ -328,9 +331,10 @@
             successIntent.setLaunchToken(token);
             try {
                 final IIntentSender successIntentTarget = ActivityManager.getService()
-                        .getIntentSender(
+                        .getIntentSenderWithFeature(
                                 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                                null /*token*/, null /*resultWho*/, 0 /*requestCode*/,
+                                callingFeatureId, null /*token*/, null /*resultWho*/,
+                                0 /*requestCode*/,
                                 new Intent[] { successIntent },
                                 new String[] { resolvedType },
                                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 3e64e98..da07365 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -661,6 +662,23 @@
             }
         }
 
+        private void ensureStrictAccessShortcutsPermission(@NonNull String callingPackage) {
+            verifyCallingPackage(callingPackage);
+            if (!injectHasAccessShortcutsPermission(injectBinderCallingPid(),
+                    injectBinderCallingUid())) {
+                throw new SecurityException("Caller can't access shortcut information");
+            }
+        }
+
+        /**
+         * Returns true if the caller has the "ACCESS_SHORTCUTS" permission.
+         */
+        @VisibleForTesting
+        boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
+            return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS,
+                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+        }
+
         @Override
         public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
                 String packageName, List shortcutIds, List<LocusId> locusIds,
@@ -710,6 +728,30 @@
         }
 
         @Override
+        public void cacheShortcuts(String callingPackage, String packageName, List<String> ids,
+                UserHandle targetUser) {
+            ensureStrictAccessShortcutsPermission(callingPackage);
+            if (!canAccessProfile(targetUser.getIdentifier(), "Cannot cache shortcuts")) {
+                return;
+            }
+
+            mShortcutServiceInternal.cacheShortcuts(getCallingUserId(),
+                    callingPackage, packageName, ids, targetUser.getIdentifier());
+        }
+
+        @Override
+        public void uncacheShortcuts(String callingPackage, String packageName, List<String> ids,
+                UserHandle targetUser) {
+            ensureStrictAccessShortcutsPermission(callingPackage);
+            if (!canAccessProfile(targetUser.getIdentifier(), "Cannot uncache shortcuts")) {
+                return;
+            }
+
+            mShortcutServiceInternal.uncacheShortcuts(getCallingUserId(),
+                    callingPackage, packageName, ids, targetUser.getIdentifier());
+        }
+
+        @Override
         public int getShortcutIconResId(String callingPackage, String packageName, String id,
                 int targetUserId) {
             ensureShortcutPermission(callingPackage);
@@ -741,8 +783,9 @@
         }
 
         @Override
-        public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
-                Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
+        public boolean startShortcut(String callingPackage, String packageName, String featureId,
+                String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
+                int targetUserId) {
             verifyCallingPackage(callingPackage);
             if (!canAccessProfile(targetUserId, "Cannot start activity")) {
                 return false;
@@ -766,15 +809,16 @@
             intents[0].setSourceBounds(sourceBounds);
 
             return startShortcutIntentsAsPublisher(
-                    intents, packageName, startActivityOptions, targetUserId);
+                    intents, packageName, featureId, startActivityOptions, targetUserId);
         }
 
         private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
-                @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
+                @NonNull String publisherPackage, @Nullable String publishedFeatureId,
+                Bundle startActivityOptions, int userId) {
             final int code;
             try {
                 code = mActivityTaskManagerInternal.startActivitiesAsPackage(publisherPackage,
-                        userId, intents, startActivityOptions);
+                        publishedFeatureId, userId, intents, startActivityOptions);
                 if (ActivityManager.isStartResultSuccessful(code)) {
                     return true; // Success
                 } else {
@@ -829,8 +873,8 @@
 
         @Override
         public void startSessionDetailsActivityAsUser(IApplicationThread caller,
-                String callingPackage, SessionInfo sessionInfo, Rect sourceBounds,
-                Bundle opts, UserHandle userHandle) throws RemoteException {
+                String callingPackage, String callingFeatureId, SessionInfo sessionInfo,
+                Rect sourceBounds, Bundle opts, UserHandle userHandle) throws RemoteException {
             int userId = userHandle.getIdentifier();
             if (!canAccessProfile(userId, "Cannot start details activity")) {
                 return;
@@ -846,13 +890,13 @@
                             .authority(callingPackage).build());
             i.setSourceBounds(sourceBounds);
 
-            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage, i, opts,
-                    userId);
+            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
+                    callingFeatureId, i, opts, userId);
         }
 
         @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
-                ComponentName component, Rect sourceBounds,
+                String callingFeatureId, ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) {
                 return;
@@ -906,12 +950,12 @@
                 Binder.restoreCallingIdentity(ident);
             }
             mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    launchIntent, opts, user.getIdentifier());
+                    callingFeatureId, launchIntent, opts, user.getIdentifier());
         }
 
         @Override
         public void showAppDetailsAsUser(IApplicationThread caller,
-                String callingPackage, ComponentName component,
+                String callingPackage, String callingFeatureId, ComponentName component,
                 Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot show app details")) {
                 return;
@@ -929,7 +973,7 @@
                 Binder.restoreCallingIdentity(ident);
             }
             mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    intent, opts, user.getIdentifier());
+                    callingFeatureId, intent, opts, user.getIdentifier());
         }
 
         /** Checks if user is a profile of or same as listeningUser.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 88c048c..2c85d06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6298,10 +6298,11 @@
 
     private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPackage,
-            boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
+            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+            Bundle verificationBundle, int userId) {
         final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
                 new InstantAppRequest(responseObj, origIntent, resolvedType,
-                        callingPackage, isRequesterInstantApp, userId, verificationBundle,
+                        callingPackage, callingFeatureId, isRequesterInstantApp, userId, verificationBundle,
                         false /*resolveForStart*/, responseObj.hostDigestPrefixSecure,
                         responseObj.token));
         mHandler.sendMessage(msg);
@@ -6987,10 +6988,10 @@
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
                 String token = UUID.randomUUID().toString();
                 InstantAppDigest digest = InstantAppResolver.parseDigest(intent);
-                final InstantAppRequest requestObject = new InstantAppRequest(
-                        null /*responseObj*/, intent /*origIntent*/, resolvedType,
-                        null /*callingPackage*/, isRequesterInstantApp,
-                        userId, null /*verificationBundle*/, resolveForStart,
+                final InstantAppRequest requestObject = new InstantAppRequest(null /*responseObj*/,
+                        intent /*origIntent*/, resolvedType, null /*callingPackage*/,
+                        null /*callingFeatureId*/, isRequesterInstantApp, userId,
+                        null /*verificationBundle*/, resolveForStart,
                         digest.getDigestPrefixSecure(), token);
                 auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
                         mInstantAppResolverConnection, requestObject);
@@ -12206,7 +12207,7 @@
                         + intent.toShortString(false, true, false, false)
                         + " " + intent.getExtras(), here);
             }
-            am.broadcastIntent(null, intent, null, finishedReceiver,
+            am.broadcastIntentWithFeature(null, null, intent, null, finishedReceiver,
                     0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE,
                     null, finishedReceiver != null, false, id);
         }
@@ -12368,8 +12369,9 @@
                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
             }
             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
-            am.broadcastIntent(null, lockedBcIntent, null, null, 0, null, null, requiredPermissions,
-                    android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+            am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
+                    requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                    userId);
 
             // Deliver BOOT_COMPLETED only if user is unlocked
             if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
@@ -12377,8 +12379,9 @@
                 if (includeStopped) {
                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                 }
-                am.broadcastIntent(null, bcIntent, null, null, 0, null, null, requiredPermissions,
-                        android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+                am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
+                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                        userId);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -18781,7 +18784,7 @@
             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             try {
-                am.broadcastIntent(null, intent, null, null,
+                am.broadcastIntentWithFeature(null, null, intent, null, null,
                         0, null, null, null, android.app.AppOpsManager.OP_NONE,
                         null, false, false, userId);
             } catch (RemoteException e) {
@@ -23425,9 +23428,10 @@
         @Override
         public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
                 Intent origIntent, String resolvedType, String callingPackage,
-                boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
-            PackageManagerService.this.requestInstantAppResolutionPhaseTwo(
-                    responseObj, origIntent, resolvedType, callingPackage, isRequesterInstantApp,
+                @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+                Bundle verificationBundle, int userId) {
+            PackageManagerService.this.requestInstantAppResolutionPhaseTwo(responseObj, origIntent,
+                    resolvedType, callingPackage, callingFeatureId, isRequesterInstantApp,
                     verificationBundle, userId);
         }
 
@@ -24337,8 +24341,9 @@
                 Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
             };
             try {
-                am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions,
-                        android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL);
+                am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
+                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                        UserHandle.USER_ALL);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d16c074..377fd16 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1704,7 +1704,7 @@
             ShortcutInfo.validateIcon(shortcut.getIcon());
         }
 
-        shortcut.replaceFlags(0);
+        shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED);
     }
 
     private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
@@ -2758,6 +2758,68 @@
         }
 
         @Override
+        public void cacheShortcuts(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId) {
+            updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds,
+                    userId, /* doCache= */ true);
+        }
+
+        @Override
+        public void uncacheShortcuts(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId) {
+            updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds,
+                    userId, /* doCache= */ false);
+        }
+
+        private void updateCachedShortcutsInternal(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId, boolean doCache) {
+            // Calling permission must be checked by LauncherAppsImpl.
+            Preconditions.checkStringNotEmpty(packageName, "packageName");
+            Objects.requireNonNull(shortcutIds, "shortcutIds");
+
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
+                throwIfUserLockedL(launcherUserId);
+
+                final int idSize = shortcutIds.size();
+                final ShortcutPackage sp = getUserShortcutsLocked(userId)
+                        .getPackageShortcutsIfExists(packageName);
+                if (idSize == 0 || sp == null) {
+                    return;
+                }
+
+                for (int i = 0; i < idSize; i++) {
+                    final String id = Preconditions.checkStringNotEmpty(shortcutIds.get(i));
+                    final ShortcutInfo si = sp.findShortcutById(id);
+                    if (si == null || doCache == si.isCached()) {
+                        continue;
+                    }
+
+                    if (doCache) {
+                        if (si.isDynamic() && si.isLongLived()) {
+                            si.addFlags(ShortcutInfo.FLAG_CACHED);
+                        } else {
+                            Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring"
+                                    + "shortcut " + si.getId());
+                        }
+                    } else {
+                        if (si.isDynamic()) {
+                            si.clearFlags(ShortcutInfo.FLAG_CACHED);
+                        } else {
+                            sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+                        }
+                    }
+                }
+            }
+            packageShortcutsChanged(packageName, userId);
+
+            verifyStates();
+        }
+
+        @Override
         public Intent[] createShortcutIntents(int launcherUserId,
                 @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId,
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 2c7795a..43d4596 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -39,9 +39,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -52,14 +50,15 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LongSparseLongArray;
-import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IntPair;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
@@ -70,6 +69,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -100,7 +100,7 @@
      * scheduled for a package/user.
      */
     @GuardedBy("mLock")
-    private final ArraySet<Pair<String, Integer>> mIsPackageSyncsScheduled = new ArraySet<>();
+    private final ArraySet<Integer> mIsPackageSyncsScheduled = new ArraySet<>();
 
     public PermissionPolicyService(@NonNull Context context) {
         super(context);
@@ -125,10 +125,8 @@
 
             @Override
             public void onPackageChanged(String packageName, int uid) {
-                final int userId = UserHandle.getUserId(uid);
-
-                if (isStarted(userId)) {
-                    synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
+                if (isStarted(UserHandle.getUserId(uid))) {
+                    synchronizePackagePermissionsAndAppOpsForUser(uid);
                 }
             }
 
@@ -139,12 +137,21 @@
         });
 
         permManagerInternal.addOnRuntimePermissionStateChangedListener(
-                this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
+                (packageName, userId) -> {
+                    int uid;
+                    try {
+                        uid = getContext().getPackageManager().getPackageUidAsUser(packageName, 0,
+                                userId);
+                    } catch (NameNotFoundException e) {
+                        Slog.e(LOG_TAG, "Cannot synchronize changed package " + packageName, e);
+                        return;
+                    }
+                    synchronizeUidPermissionsAndAppOpsAsync(uid);
+                });
 
         mAppOpsCallback = new IAppOpsCallback.Stub() {
             public void opChanged(int op, int uid, String packageName) {
-                synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
-                        UserHandle.getUserId(uid));
+                synchronizeUidPermissionsAndAppOpsAsync(uid);
             }
         };
 
@@ -194,19 +201,17 @@
         return AppOpsManager.opToSwitch(op);
     }
 
-    private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
-            @UserIdInt int changedUserId) {
-        if (isStarted(changedUserId)) {
+    private void synchronizeUidPermissionsAndAppOpsAsync(int uid) {
+        if (isStarted(UserHandle.getUserId(uid))) {
             synchronized (mLock) {
-                if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {
+                if (mIsPackageSyncsScheduled.add(uid)) {
                     FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
                             PermissionPolicyService
                                     ::synchronizePackagePermissionsAndAppOpsForUser,
-                            this, packageName, changedUserId));
+                            this, uid));
                 } else {
                     if (DEBUG) {
-                        Slog.v(LOG_TAG, "sync for " + packageName + "/" + changedUserId
-                                + " already scheduled");
+                        Slog.v(LOG_TAG, "sync for " + uid + " already scheduled");
                     }
                 }
             }
@@ -335,39 +340,20 @@
     /**
      * Synchronize a single package.
      */
-    private void synchronizePackagePermissionsAndAppOpsForUser(@NonNull String packageName,
-            @UserIdInt int userId) {
+    private void synchronizePackagePermissionsAndAppOpsForUser(int uid) {
         synchronized (mLock) {
-            mIsPackageSyncsScheduled.remove(new Pair<>(packageName, userId));
+            mIsPackageSyncsScheduled.remove(uid);
         }
 
         if (DEBUG) {
             Slog.v(LOG_TAG,
-                    "synchronizePackagePermissionsAndAppOpsForUser(" + packageName + ", "
-                            + userId + ")");
+                    "synchronizePackagePermissionsAndAppOpsForUser(" + uid + ")");
         }
 
-        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
-                PackageManagerInternal.class);
-        final PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName, 0,
-                Process.SYSTEM_UID, userId);
-        if (pkg == null) {
-            return;
-        }
         final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(
-                getUserContext(getContext(), UserHandle.of(userId)));
-        synchroniser.addPackage(pkg.packageName);
-        final String[] sharedPkgNames = packageManagerInternal.getSharedUserPackagesForPackage(
-                pkg.packageName, userId);
-
-        for (String sharedPkgName : sharedPkgNames) {
-            final AndroidPackage sharedPkg = packageManagerInternal
-                    .getPackage(sharedPkgName);
-            if (sharedPkg != null) {
-                synchroniser.addPackage(sharedPkg.getPackageName());
-            }
-        }
-        synchroniser.syncPackages();
+                getUserContext(getContext(), UserHandle.getUserHandleForUid(uid)));
+        synchroniser.addUid(uid);
+        synchroniser.syncUids();
     }
 
     /**
@@ -381,8 +367,8 @@
         final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
                 getUserContext(getContext(), UserHandle.of(userId)));
         packageManagerInternal.forEachPackage(
-                (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
-        synchronizer.syncPackages();
+                (pkg) -> synchronizer.addUid(pkg.getUid()));
+        synchronizer.syncUids();
     }
 
     /**
@@ -397,37 +383,51 @@
 
         private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
 
+        // Cache uid -> packageNames
+        private SparseArray<String[]> mUidToPkg = new SparseArray<>();
+
         /**
          * All ops that need to be flipped to allow.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToAllow = new ArraySet<>();
 
         /**
          * All ops that need to be flipped to ignore.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToIgnore = new ArraySet<>();
 
         /**
          * All ops that need to be flipped to ignore if not allowed.
          *
          * Currently, only used by soft restricted permissions logic.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToIgnoreIfNotAllowed = new ArraySet<>();
 
         /**
          * All ops that need to be flipped to foreground.
          *
          * Currently, only used by the foreground/background permissions logic.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToForeground = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToForeground = new ArraySet<>();
+
+        private @Nullable String[] getPackageNamesForUid(int uid) {
+            String[] pkgs = mUidToPkg.get(uid);
+            if (pkgs != null) {
+                return pkgs;
+            }
+
+            pkgs = mPackageManager.getPackagesForUid(uid);
+            mUidToPkg.put(uid, pkgs);
+            return pkgs;
+        }
 
         PermissionToOpSynchroniser(@NonNull Context context) {
             mContext = context;
@@ -449,11 +449,11 @@
         }
 
         /**
-         * Set app ops that were added in {@link #addPackage}.
+         * Set app ops that were added in {@link #addUid}.
          *
          * <p>This processes ops previously added by {@link #addAppOps(PackageInfo, String)}
          */
-        private void syncPackages() {
+        private void syncUids() {
             // Remember which ops were already set. This makes sure that we always set the most
             // permissive mode if two OpChanges are scheduled. This can e.g. happen if two
             // permissions change the same op. See {@link #getSwitchOp}.
@@ -461,42 +461,42 @@
 
             final int allowCount = mOpsToAllow.size();
             for (int i = 0; i < allowCount; i++) {
-                final OpToChange op = mOpsToAllow.get(i);
+                final OpToChange op = mOpsToAllow.valueAt(i);
 
-                setUidModeAllowed(op.code, op.uid, op.packageName);
+                setUidModeAllowed(op.code, op.uid);
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
             final int foregroundCount = mOpsToForeground.size();
             for (int i = 0; i < foregroundCount; i++) {
-                final OpToChange op = mOpsToForeground.get(i);
+                final OpToChange op = mOpsToForeground.valueAt(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                setUidModeForeground(op.code, op.uid, op.packageName);
+                setUidModeForeground(op.code, op.uid);
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
             final int ignoreCount = mOpsToIgnore.size();
             for (int i = 0; i < ignoreCount; i++) {
-                final OpToChange op = mOpsToIgnore.get(i);
+                final OpToChange op = mOpsToIgnore.valueAt(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                setUidModeIgnored(op.code, op.uid, op.packageName);
+                setUidModeIgnored(op.code, op.uid);
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
             final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
             for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
-                final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
+                final OpToChange op = mOpsToIgnoreIfNotAllowed.valueAt(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
+                boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid);
                 if (wasSet) {
                     alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
                 }
@@ -555,7 +555,7 @@
             }
 
             int uid = packageInfo.applicationInfo.uid;
-            OpToChange opToChange = new OpToChange(uid, packageName, appOpCode);
+            OpToChange opToChange = new OpToChange(uid, appOpCode);
             switch (appOpMode) {
                 case MODE_ALLOWED:
                     mOpsToAllow.add(opToChange);
@@ -618,8 +618,7 @@
             }
 
             int uid = packageInfo.applicationInfo.uid;
-            String packageName = packageInfo.packageName;
-            OpToChange extraOpToChange = new OpToChange(uid, packageName, extraOpCode);
+            OpToChange extraOpToChange = new OpToChange(uid, extraOpCode);
             if (policy.mayAllowExtraAppOp()) {
                 mOpsToAllow.add(extraOpToChange);
             } else {
@@ -632,45 +631,56 @@
         }
 
         /**
-         * Add a package for {@link #syncPackages() processing} later.
+         * Add a Uid for {@link #syncUids() processing} later.
          *
          * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
          *
-         * @param pkgName The package to add for later processing.
+         * @param uid The uid to add for later processing.
          */
-        void addPackage(@NonNull String pkgName) {
-            final PackageInfo pkg;
-            try {
-                pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
-            } catch (NameNotFoundException e) {
+        void addUid(int uid) {
+            String[] pkgNames = getPackageNamesForUid(uid);
+            if (pkgNames == null) {
                 return;
             }
 
-            if (pkg.requestedPermissions == null) {
-                return;
-            }
+            for (String pkgName : pkgNames) {
+                final PackageInfo pkg;
+                try {
+                    pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
+                } catch (NameNotFoundException e) {
+                    continue;
+                }
 
-            for (String permission : pkg.requestedPermissions) {
-                addAppOps(pkg, permission);
+                if (pkg.requestedPermissions == null) {
+                    continue;
+                }
+
+                for (String permission : pkg.requestedPermissions) {
+                    addAppOps(pkg, permission);
+                }
             }
         }
 
-        private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
-            setUidMode(opCode, uid, MODE_ALLOWED, packageName);
+        private void setUidModeAllowed(int opCode, int uid) {
+            setUidMode(opCode, uid, MODE_ALLOWED);
         }
 
-        private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
-            setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
+        private void setUidModeForeground(int opCode, int uid) {
+            setUidMode(opCode, uid, MODE_FOREGROUND);
         }
 
-        private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
-            setUidMode(opCode, uid, MODE_IGNORED, packageName);
+        private void setUidModeIgnored(int opCode, int uid) {
+            setUidMode(opCode, uid, MODE_IGNORED);
         }
 
-        private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
-                @NonNull String packageName) {
+        private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid) {
+            String[] pkgsOfUid = getPackageNamesForUid(uid);
+            if (ArrayUtils.isEmpty(pkgsOfUid)) {
+                return false;
+            }
+
             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
-                    opCode), uid, packageName);
+                    opCode), uid, pkgsOfUid[0]);
             if (currentMode != MODE_ALLOWED) {
                 if (currentMode != MODE_IGNORED) {
                     mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, MODE_IGNORED,
@@ -681,20 +691,24 @@
             return false;
         }
 
-        private void setUidMode(int opCode, int uid, int mode,
-                @NonNull String packageName) {
+        private void setUidMode(int opCode, int uid, int mode) {
+            String[] pkgsOfUid = getPackageNamesForUid(uid);
+            if (ArrayUtils.isEmpty(pkgsOfUid)) {
+                return;
+            }
+
             final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
-                    opCode), uid, packageName);
+                    opCode), uid, pkgsOfUid[0]);
             if (oldMode != mode) {
                 mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, mode,
                         mAppOpsCallback);
                 final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
-                        opCode), uid, packageName);
+                        opCode), uid, pkgsOfUid[0]);
                 if (newMode != mode) {
                     // Work around incorrectly-set package mode. It never makes sense for app ops
                     // related to runtime permissions, but can get in the way and we have to reset
                     // it.
-                    mAppOpsManagerInternal.setModeIgnoringCallback(opCode, uid, packageName,
+                    mAppOpsManagerInternal.setModeIgnoringCallback(opCode, uid, pkgsOfUid[0],
                             AppOpsManager.opToDefaultMode(opCode), mAppOpsCallback);
                 }
             }
@@ -702,14 +716,30 @@
 
         private class OpToChange {
             final int uid;
-            final @NonNull String packageName;
             final int code;
 
-            OpToChange(int uid, @NonNull String packageName, int code) {
+            OpToChange(int uid, int code) {
                 this.uid = uid;
-                this.packageName = packageName;
                 this.code = code;
             }
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) {
+                    return true;
+                }
+                if (o == null || getClass() != o.getClass()) {
+                    return false;
+                }
+
+                OpToChange other = (OpToChange) o;
+                return uid == other.uid && code == other.code;
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(uid, code);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ede04f3..4b2dc85 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5195,7 +5195,8 @@
                     final Intent dock = createHomeDockIntent();
                     if (dock != null) {
                         int result = ActivityTaskManager.getService()
-                                .startActivityAsUser(null, mContext.getBasePackageName(), dock,
+                                .startActivityAsUser(null, mContext.getBasePackageName(),
+                                        mContext.getFeatureId(), dock,
                                         dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                         null, null, 0,
                                         ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5206,7 +5207,8 @@
                     }
                 }
                 int result = ActivityTaskManager.getService()
-                        .startActivityAsUser(null, mContext.getBasePackageName(), mHomeIntent,
+                        .startActivityAsUser(null, mContext.getBasePackageName(),
+                                mContext.getFeatureId(), mHomeIntent,
                                 mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                 null, null, 0,
                                 ActivityManager.START_FLAG_ONLY_IF_NEEDED,
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index f3f14a9..1be6f22 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -38,9 +38,9 @@
 import android.content.rollback.RollbackInfo;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.PackageWatchdog;
 
 import java.util.ArrayList;
@@ -206,12 +206,25 @@
                 + " rollbackReason: " + rollbackReasonToString(rollbackReason)
                 + " failedPackageName: " + failingPackageName);
         if (logPackage != null) {
-            StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
-                    logPackage.getVersionCode(), rollbackReason, failingPackageName);
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                    type,
+                    logPackage.getPackageName(),
+                    logPackage.getVersionCode(),
+                    rollbackReason,
+                    failingPackageName,
+                    new byte[]{});
         } else {
             // In the case that the log package is null, still log an empty string as an
             // indication that retrieving the logging parent failed.
-            StatsLog.logWatchdogRollbackOccurred(type, "", 0, rollbackReason, failingPackageName);
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                    type,
+                    "",
+                    0,
+                    rollbackReason,
+                    failingPackageName,
+                    new byte[]{});
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a54f5d4..ad57e1f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -362,6 +362,7 @@
     private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
     private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
     private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+    private static final String ATTR_LAUNCHEDFROMFEATURE = "launched_from_feature";
     private static final String ATTR_RESOLVEDTYPE = "resolved_type";
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
@@ -422,6 +423,7 @@
     final int launchedFromPid; // always the pid who started the activity.
     final int launchedFromUid; // always the uid who started the activity.
     final String launchedFromPackage; // always the package who started the activity.
+    final @Nullable String launchedFromFeatureId; // always the feature in launchedFromPackage
     final Intent intent;    // the original intent that generated us
     final String shortComponentName; // the short component name of the intent
     final String resolvedType; // as per original caller;
@@ -773,6 +775,7 @@
                 pw.print(" processName="); pw.println(processName);
         pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
                 pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
+                pw.print(" launchedFromFeature="); pw.print(launchedFromFeatureId);
                 pw.print(" userId="); pw.println(mUserId);
         pw.print(prefix); pw.print("app="); pw.println(app);
         pw.print(prefix); pw.println(intent.toInsecureString());
@@ -1483,9 +1486,10 @@
     }
 
     ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
-            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
-            String _resolvedType, ActivityInfo aInfo, Configuration _configuration,
-            ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
+            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
+            @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
+            ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
+            String _resultWho, int _reqCode, boolean _componentSpecified,
             boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
             ActivityOptions options, ActivityRecord sourceRecord) {
         super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
@@ -1564,6 +1568,7 @@
         launchedFromPid = _launchedFromPid;
         launchedFromUid = _launchedFromUid;
         launchedFromPackage = _launchedFromPackage;
+        launchedFromFeatureId = _launchedFromFeature;
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
         componentSpecified = _componentSpecified;
@@ -2284,7 +2289,7 @@
      * @return Whether AppOps allows this package to enter picture-in-picture.
      */
     private boolean checkEnterPictureInPictureAppOpsState() {
-        return mAtmService.getAppOpsService().checkOperation(
+        return mAtmService.getAppOpsManager().checkOpNoThrow(
                 OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
     }
 
@@ -7282,6 +7287,9 @@
         if (launchedFromPackage != null) {
             out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
         }
+        if (launchedFromFeatureId != null) {
+            out.attribute(null, ATTR_LAUNCHEDFROMFEATURE, launchedFromFeatureId);
+        }
         if (resolvedType != null) {
             out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
         }
@@ -7309,6 +7317,7 @@
         PersistableBundle persistentState = null;
         int launchedFromUid = 0;
         String launchedFromPackage = null;
+        String launchedFromFeature = null;
         String resolvedType = null;
         boolean componentSpecified = false;
         int userId = 0;
@@ -7327,6 +7336,8 @@
                 launchedFromUid = Integer.parseInt(attrValue);
             } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
                 launchedFromPackage = attrValue;
+            } else if (ATTR_LAUNCHEDFROMFEATURE.equals(attrName)) {
+                launchedFromFeature = attrValue;
             } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
                 resolvedType = attrValue;
             } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
@@ -7374,10 +7385,11 @@
                     " resolvedType=" + resolvedType);
         }
         final ActivityRecord r = new ActivityRecord(service, null /* caller */,
-                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
-                aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
-                0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
-                stackSupervisor, null /* options */, null /* sourceRecord */);
+                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, launchedFromFeature,
+                intent, resolvedType, aInfo, service.getConfiguration(), null /* resultTo */,
+                null /* resultWho */, 0 /* reqCode */, componentSpecified,
+                false /* rootVoiceInteraction */, stackSupervisor, null /* options */,
+                null /* sourceRecord */);
 
         r.mPersistentState = persistentState;
         r.taskDescription = taskDescription;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d380f8c..9030fce 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -135,6 +135,7 @@
 import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -622,7 +623,7 @@
                 true /*neverRelinquishIdentity*/,
                 _taskDescription != null ? _taskDescription : new ActivityManager.TaskDescription(),
                 id, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
-                info.applicationInfo.uid, info.packageName, info.resizeMode,
+                info.applicationInfo.uid, info.packageName, null, info.resizeMode,
                 info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
                 false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
                 _voiceSession, _voiceInteractor, stack);
@@ -635,15 +636,16 @@
             String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
             ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
             int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid,
-            String callingPackage, int resizeMode, boolean supportsPictureInPicture,
-            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
+            String callingPackage, @Nullable String callingFeatureId, int resizeMode,
+            boolean supportsPictureInPicture, boolean _realActivitySuspended,
+            boolean userSetupComplete, int minWidth, int minHeight,
             ActivityInfo info, IVoiceInteractionSession _voiceSession,
             IVoiceInteractor _voiceInteractor, ActivityStack stack) {
         super(atmService, id, _intent, _affinityIntent, _affinity, _rootAffinity,
                 _realActivity, _origActivity, _rootWasReset, _autoRemoveRecents, _askedCompatMode,
                 _userId, _effectiveUid, _lastDescription, lastTimeMoved, neverRelinquishIdentity,
                 _lastTaskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
-                callingUid, callingPackage, resizeMode, supportsPictureInPicture,
+                callingUid, callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
                 _realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
                 _voiceInteractor, stack);
 
@@ -2924,6 +2926,7 @@
                             .setCallingPid(-1)
                             .setCallingUid(parent.launchedFromUid)
                             .setCallingPackage(parent.launchedFromPackage)
+                            .setCallingFeatureId(parent.launchedFromFeatureId)
                             .setRealCallingPid(-1)
                             .setRealCallingUid(parent.launchedFromUid)
                             .setComponentSpecified(true)
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a513ef8..a582f21 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -88,6 +88,7 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -260,7 +261,9 @@
     /** Short cut */
     private WindowManagerService mWindowManager;
 
-     /** Common synchronization logic used to save things to disks. */
+    private AppOpsManager mAppOpsManager;
+
+    /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
     LaunchParamsPersister mLaunchParamsPersister;
     private LaunchParamsController mLaunchParamsController;
@@ -1047,8 +1050,8 @@
 
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
             int requestCode, int callingPid, int callingUid, String callingPackage,
-            boolean ignoreTargetSecurity, boolean launchingInTask,
-            WindowProcessController callerApp, ActivityRecord resultRecord,
+            @Nullable String callingFeatureId, boolean ignoreTargetSecurity,
+            boolean launchingInTask, WindowProcessController callerApp, ActivityRecord resultRecord,
             ActivityStack resultStack) {
         final boolean isCallerRecents = mService.getRecentTasks() != null
                 && mService.getRecentTasks().isCallerRecents(callingUid);
@@ -1060,10 +1063,10 @@
             // existing task, then also allow the activity to be fully relaunched.
             return true;
         }
-        final int componentRestriction = getComponentRestrictionForCallingPackage(
-                aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
+        final int componentRestriction = getComponentRestrictionForCallingPackage(aInfo,
+                callingPackage, callingFeatureId, callingPid, callingUid, ignoreTargetSecurity);
         final int actionRestriction = getActionRestrictionForCallingPackage(
-                intent.getAction(), callingPackage, callingPid, callingUid);
+                intent.getAction(), callingPackage, callingFeatureId, callingPid, callingUid);
         if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
@@ -1194,8 +1197,16 @@
         }
     }
 
+    private AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mService.mContext.getSystemService(AppOpsManager.class);
+        }
+        return mAppOpsManager;
+    }
+
     private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
-            String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
+            String callingPackage, @Nullable String callingFeatureId, int callingPid,
+            int callingUid, boolean ignoreTargetSecurity) {
         if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
                 callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
                 == PERMISSION_DENIED) {
@@ -1211,9 +1222,8 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        // TODO moltmann b/136595429: Set featureId from caller
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
-                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
+        if (getAppOpsManager().noteOpNoThrow(opCode, callingUid,
+                callingPackage, callingFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
             if (!ignoreTargetSecurity) {
                 return ACTIVITY_RESTRICTION_APPOP;
             }
@@ -1222,8 +1232,8 @@
         return ACTIVITY_RESTRICTION_NONE;
     }
 
-    private int getActionRestrictionForCallingPackage(String action,
-            String callingPackage, int callingPid, int callingUid) {
+    private int getActionRestrictionForCallingPackage(String action, String callingPackage,
+            @Nullable String callingFeatureId, int callingPid, int callingUid) {
         if (action == null) {
             return ACTIVITY_RESTRICTION_NONE;
         }
@@ -1256,9 +1266,8 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        // TODO moltmann b/136595429: Set featureId from caller
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
-                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
+        if (getAppOpsManager().noteOpNoThrow(opCode, callingUid,
+                callingPackage, callingFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
             return ACTIVITY_RESTRICTION_APPOP;
         }
 
@@ -2701,6 +2710,7 @@
             SafeActivityOptions options) {
         Task task = null;
         final String callingPackage;
+        final String callingFeatureId;
         final Intent intent;
         final int userId;
         int activityType = ACTIVITY_TYPE_UNDEFINED;
@@ -2780,11 +2790,12 @@
                 return ActivityManager.START_TASK_TO_FRONT;
             }
             callingPackage = task.mCallingPackage;
+            callingFeatureId = task.mCallingFeatureId;
             intent = task.intent;
             intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
             userId = task.mUserId;
-            return mService.getActivityStartController().startActivityInPackage(
-                    task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
+            return mService.getActivityStartController().startActivityInPackage(task.mCallingUid,
+                    callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
                     null, 0, 0, options, userId, task, "startActivityFromRecents",
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
                     false /* allowBackgroundActivityStart */);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index f35ba9e..881dc81 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -26,6 +26,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
@@ -278,10 +279,11 @@
     }
 
     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-            int userId, Task inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+            String callingPackage, @Nullable String callingFeatureId, Intent intent,
+            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -292,6 +294,7 @@
                 .setRealCallingPid(realCallingPid)
                 .setRealCallingUid(realCallingUid)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -310,19 +313,20 @@
      *
      * @param uid Make a call as if this UID did.
      * @param callingPackage Make a call as if this package did.
+     * @param callingFeatureId Make a call as if this feature in the package did.
      * @param intents Intents to start.
      * @param userId Start the intents on this user.
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
      */
-    final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            boolean allowBackgroundActivityStart) {
+    final int startActivitiesInPackage(int uid, String callingPackage,
+            @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
+            IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
-            callingPackage, intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
-            originatingPendingIntent, allowBackgroundActivityStart);
+                callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
+                validateIncomingUser, originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     /**
@@ -339,9 +343,9 @@
      *        null if not originated by PendingIntent
      */
     final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            SafeActivityOptions options, int userId, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent,
+            String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart) {
 
         final String reason = "startActivityInPackage";
@@ -350,14 +354,14 @@
                 Binder.getCallingUid(), reason);
 
         // TODO: Switch to user app stacks here.
-        return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage, intents,
-                resolvedTypes, resultTo, options, userId, reason, originatingPendingIntent,
-                allowBackgroundActivityStart);
+        return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
+                callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
+                originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
-            int incomingRealCallingUid, String callingPackage, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
+            int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
             int userId, String reason, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart) {
         if (intents == null) {
@@ -435,6 +439,7 @@
                         .setCallingPid(callingPid)
                         .setCallingUid(callingUid)
                         .setCallingPackage(callingPackage)
+                        .setCallingFeatureId(callingFeatureId)
                         .setRealCallingPid(realCallingPid)
                         .setRealCallingUid(realCallingUid)
                         .setActivityOptions(checkedOptions)
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 76aa1d1..1009771 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -34,6 +34,7 @@
 
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -85,6 +86,7 @@
     private int mUserId;
     private int mStartFlags;
     private String mCallingPackage;
+    private @Nullable String mCallingFeatureId;
 
     /*
      * Per-intent states that were load from ActivityStarter and are subject to modifications
@@ -120,19 +122,20 @@
      * method should not be changed during intercept.
      */
     void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
-            String callingPackage) {
+            String callingPackage, @Nullable String callingFeatureId) {
         mRealCallingPid = realCallingPid;
         mRealCallingUid = realCallingUid;
         mUserId = userId;
         mStartFlags = startFlags;
         mCallingPackage = callingPackage;
+        mCallingFeatureId = callingFeatureId;
     }
 
     private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
         Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
         final IIntentSender target = mService.getIntentSenderLocked(
-                INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
-                null /*resultCode*/, 0 /*requestCode*/,
+                INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingFeatureId, callingUid, mUserId,
+                null /*token*/, null /*resultCode*/, 0 /*requestCode*/,
                 new Intent[] { mIntent }, new String[] { mResolvedType },
                 flags, activityOptions);
         return new IntentSender(target);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7270f2..8ed798c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -318,6 +318,7 @@
         int callingPid = DEFAULT_CALLING_PID;
         int callingUid = DEFAULT_CALLING_UID;
         String callingPackage;
+        @Nullable String callingFeatureId;
         int realCallingPid = DEFAULT_REAL_CALLING_PID;
         int realCallingUid = DEFAULT_REAL_CALLING_UID;
         int startFlags;
@@ -367,6 +368,7 @@
             callingPid = DEFAULT_CALLING_PID;
             callingUid = DEFAULT_CALLING_UID;
             callingPackage = null;
+            callingFeatureId = null;
             realCallingPid = DEFAULT_REAL_CALLING_PID;
             realCallingUid = DEFAULT_REAL_CALLING_UID;
             startFlags = 0;
@@ -405,6 +407,7 @@
             callingPid = request.callingPid;
             callingUid = request.callingUid;
             callingPackage = request.callingPackage;
+            callingFeatureId = request.callingFeatureId;
             realCallingPid = request.realCallingPid;
             realCallingUid = request.realCallingUid;
             startFlags = request.startFlags;
@@ -693,9 +696,10 @@
         }
 
         final IIntentSender target = mService.getIntentSenderLocked(
-                ActivityManager.INTENT_SENDER_ACTIVITY, "android" /* packageName */, appCallingUid,
-                mRequest.userId, null /* token */, null /* resultWho*/, 0 /* requestCode*/,
-                new Intent[] { mRequest.intent }, new String[] { mRequest.resolvedType },
+                ActivityManager.INTENT_SENDER_ACTIVITY, "android" /* packageName */,
+                null /* featureId */, appCallingUid, mRequest.userId, null /* token */,
+                null /* resultWho*/, 0 /* requestCode*/, new Intent[]{mRequest.intent},
+                new String[]{mRequest.resolvedType},
                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT,
                 null /* bOptions */);
 
@@ -807,6 +811,7 @@
         int callingPid = request.callingPid;
         int callingUid = request.callingUid;
         String callingPackage = request.callingPackage;
+        String callingFeatureId = request.callingFeatureId;
         final int realCallingPid = request.realCallingPid;
         final int realCallingUid = request.realCallingUid;
         final int startFlags = request.startFlags;
@@ -882,6 +887,7 @@
                 // we want the final activity to consider it to have been launched by the
                 // previous app activity.
                 callingPackage = sourceRecord.launchedFromPackage;
+                callingFeatureId = sourceRecord.launchedFromFeatureId;
             }
         }
 
@@ -949,8 +955,8 @@
         }
 
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, request.ignoreTargetSecurity,
-                inTask != null, callerApp, resultRecord, resultStack);
+                requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
+                request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultStack);
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
         abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
@@ -990,7 +996,8 @@
             }
         }
 
-        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
+        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
+                callingFeatureId);
         if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
                 callingUid, checkedOptions)) {
             // activity start was intercepted, e.g. because the target user is currently in quiet
@@ -1023,7 +1030,7 @@
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 final IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
@@ -1081,7 +1088,7 @@
         // app [on install success].
         if (rInfo != null && rInfo.auxiliaryInfo != null) {
             intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
-                    callingPackage, verificationBundle, resolvedType, userId);
+                    callingPackage, callingFeatureId, verificationBundle, resolvedType, userId);
             resolvedType = null;
             callingUid = realCallingUid;
             callingPid = realCallingPid;
@@ -1090,9 +1097,10 @@
         }
 
         final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
-                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
-                resultRecord, resultWho, requestCode, request.componentSpecified,
-                voiceSession != null, mSupervisor, checkedOptions, sourceRecord);
+                callingPackage, callingFeatureId, intent, resolvedType, aInfo,
+                mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,
+                request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,
+                sourceRecord);
         mLastStartActivityRecord = r;
 
         if (r.appTimeTracker == null && sourceRecord != null) {
@@ -1302,21 +1310,22 @@
      * Creates a launch intent for the given auxiliary resolution data.
      */
     private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
-            Intent originalIntent, String callingPackage, Bundle verificationBundle,
-            String resolvedType, int userId) {
+            Intent originalIntent, String callingPackage, @Nullable String callingFeatureId,
+            Bundle verificationBundle, String resolvedType, int userId) {
         if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
             // request phase two resolution
             PackageManagerInternal packageManager = mService.getPackageManagerInternalLocked();
             boolean isRequesterInstantApp = packageManager.isInstantApp(callingPackage, userId);
             packageManager.requestInstantAppResolutionPhaseTwo(
                     auxiliaryResponse, originalIntent, resolvedType, callingPackage,
-                    isRequesterInstantApp, verificationBundle, userId);
+                    callingFeatureId, isRequesterInstantApp, verificationBundle, userId);
         }
         return InstantAppResolver.buildEphemeralInstallerIntent(
                 originalIntent,
                 InstantAppResolver.sanitizeIntent(originalIntent),
                 auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
                 callingPackage,
+                callingFeatureId,
                 verificationBundle,
                 resolvedType,
                 userId,
@@ -2575,6 +2584,11 @@
         return this;
     }
 
+    ActivityStarter setCallingFeatureId(String callingFeatureId) {
+        mRequest.callingFeatureId = callingFeatureId;
+        return this;
+    }
+
     /**
      * Sets the pid of the caller who requested to launch the activity.
      *
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 25f6d6f..7302e52 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -182,14 +182,15 @@
     public abstract void notifySingleTaskDisplayDrawn(int displayId);
 
     /**
-     * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
+     * Start activity {@code intents} as if {@code packageName/featureId} on user {@code userId} did
+     * it.
      *
      * - DO NOT call it with the calling UID cleared.
      * - All the necessary caller permission checks must be done at callsites.
      *
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
-    public abstract int startActivitiesAsPackage(String packageName,
+    public abstract int startActivitiesAsPackage(String packageName, String featureId,
             int userId, Intent[] intents, Bundle bOptions);
 
     /**
@@ -199,6 +200,7 @@
      * @param realCallingPid PID of the real caller.
      * @param realCallingUid UID of the real caller.
      * @param callingPackage Make a call as if this package did.
+     * @param callingFeatureId Make a call as if this feature in the package did.
      * @param intents Intents to start.
      * @param userId Start the intents on this user.
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
@@ -208,16 +210,17 @@
      *        from originatingPendingIntent
      */
     public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            SafeActivityOptions options, int userId, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent,
+            String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart);
 
     public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-            int userId, Task inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart);
+            String callingPackage, @Nullable String callingFeaturId, Intent intent,
+            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart);
 
     /**
      * Start activity {@code intent} without calling user-id check.
@@ -228,7 +231,7 @@
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
     public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, @Nullable Bundle options, int userId);
+            @Nullable String callingFeatureId, Intent intent, @Nullable Bundle options, int userId);
 
     /**
      * Called when Keyguard flags might have changed.
@@ -388,7 +391,7 @@
     public abstract ActivityTokens getTopActivityForTask(int taskId);
 
     public abstract IIntentSender getIntentSender(int type, String packageName,
-            int callingUid, int userId, IBinder token, String resultWho,
+            @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
             Bundle bOptions);
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5f3e3a3..ea5a71a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -234,7 +234,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.logging.MetricsLogger;
@@ -264,7 +263,6 @@
 import com.android.server.am.PendingIntentController;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
-import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.pm.UserManagerService;
@@ -378,7 +376,7 @@
     RootWindowContainer mRootWindowContainer;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
-    private AppOpsService mAppOpsService;
+    private AppOpsManager mAppOpsManager;
     /** All active uids in the system. */
     private final MirrorActiveUids mActiveUids = new MirrorActiveUids();
     private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
@@ -891,12 +889,11 @@
         return mUserManager;
     }
 
-    AppOpsService getAppOpsService() {
-        if (mAppOpsService == null) {
-            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
-            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+    AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         }
-        return mAppOpsService;
+        return mAppOpsManager;
     }
 
     boolean hasUserRestriction(String restriction, int userId) {
@@ -904,8 +901,8 @@
     }
 
     boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
-        final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                callingUid, callingPackage, /* featureId */ null, false, "");
+        final int mode = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                callingUid, callingPackage, /* featureId */ null, "");
         if (mode == AppOpsManager.MODE_DEFAULT) {
             return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
                     == PERMISSION_GRANTED;
@@ -1025,41 +1022,43 @@
 
     @Override
     public final int startActivity(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions,
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions) {
+        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                 UserHandle.getCallingUserId());
     }
 
     @Override
     public final int startActivities(IApplicationThread caller, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
-            int userId) {
+            String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
+            Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         final String reason = "startActivities";
         enforceNotIsolatedCaller(reason);
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
         // TODO: Switch to user app stacks here.
         return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
-                intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
-                reason, null /* originatingPendingIntent */,
-                false /* allowBackgroundActivityStart */);
+                callingFeatureId, intents, resolvedTypes, resultTo,
+                SafeActivityOptions.fromBundle(bOptions), userId, reason,
+                null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */);
     }
 
     @Override
     public int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                 true /*validateIncomingUser*/);
     }
 
     private int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
-            boolean validateIncomingUser) {
+            @Nullable String callingFeatureId, Intent intent, String resolvedType,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
 
@@ -1070,6 +1069,7 @@
         return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1215,6 +1215,7 @@
                     .setCallingPid(-1)
                     .setCallingUid(r.launchedFromUid)
                     .setCallingPackage(r.launchedFromPackage)
+                    .setCallingFeatureId(r.launchedFromFeatureId)
                     .setRealCallingPid(-1)
                     .setRealCallingUid(r.launchedFromUid)
                     .setActivityOptions(options)
@@ -1231,8 +1232,9 @@
 
     @Override
     public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         final WaitResult res = new WaitResult();
         enforceNotIsolatedCaller("startActivityAndWait");
@@ -1242,6 +1244,7 @@
         getActivityStartController().obtainStarter(intent, "startActivityAndWait")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1257,8 +1260,9 @@
 
     @Override
     public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, Configuration config, Bundle bOptions, int userId) {
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, Configuration config,
+            Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityWithConfig");
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
@@ -1267,6 +1271,7 @@
         return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1317,6 +1322,7 @@
         final ActivityRecord sourceRecord;
         final int targetUid;
         final String targetPackage;
+        final String targetFeatureId;
         final boolean isResolver;
         synchronized (mGlobalLock) {
             if (resultTo == null) {
@@ -1384,6 +1390,7 @@
             }
             targetUid = sourceRecord.launchedFromUid;
             targetPackage = sourceRecord.launchedFromPackage;
+            targetFeatureId = sourceRecord.launchedFromFeatureId;
             isResolver = sourceRecord.isResolverOrChildActivity();
         }
 
@@ -1396,6 +1403,7 @@
             return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
                     .setCallingUid(targetUid)
                     .setCallingPackage(targetPackage)
+                    .setCallingFeatureId(targetFeatureId)
                     .setResolvedType(resolvedType)
                     .setResultTo(resultTo)
                     .setResultWho(resultWho)
@@ -1431,8 +1439,8 @@
     }
 
     @Override
-    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, IVoiceInteractionSession session,
+    public int startVoiceActivity(String callingPackage, String callingFeatureId, int callingPid,
+            int callingUid, Intent intent, String resolvedType, IVoiceInteractionSession session,
             IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
@@ -1445,6 +1453,7 @@
         return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setVoiceSession(session)
                 .setVoiceInteractor(interactor)
@@ -1457,8 +1466,9 @@
     }
 
     @Override
-    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, Bundle bOptions, int userId) {
+    public int startAssistantActivity(String callingPackage, @NonNull String callingFeatureId,
+            int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions,
+            int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
         userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
@@ -1466,6 +1476,7 @@
         return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
                 .setUserId(userId)
@@ -1489,13 +1500,14 @@
         try {
             synchronized (mGlobalLock) {
                 final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+                final String recentsFeatureId = mRecentTasks.getRecentsComponentFeatureId();
                 final int recentsUid = mRecentTasks.getRecentsComponentUid();
                 final WindowProcessController caller = getProcessController(callingPid, callingUid);
 
                 // Start a new recents animation
                 final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
                         getActivityStartController(), mWindowManager, intent, recentsComponent,
-                        recentsUid, caller);
+                        recentsFeatureId, recentsUid, caller);
                 if (recentsAnimationRunner == null) {
                     anim.preloadRecentsActivity();
                 } else {
@@ -5773,9 +5785,9 @@
 
     }
 
-    IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId,
-            IBinder token, String resultWho, int requestCode, Intent[] intents,
-            String[] resolvedTypes, int flags, Bundle bOptions) {
+    IIntentSender getIntentSenderLocked(int type, String packageName, String featureId,
+            int callingUid, int userId, IBinder token, String resultWho, int requestCode,
+            Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions) {
 
         ActivityRecord activity = null;
         if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
@@ -5791,8 +5803,8 @@
         }
 
         final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
-                callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                bOptions);
+                featureId, callingUid, userId, token, resultWho, requestCode, intents,
+                resolvedTypes, flags, bOptions);
         final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
         if (noCreate) {
             return rec;
@@ -6180,8 +6192,8 @@
         }
 
         @Override
-        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
-                Bundle bOptions) {
+        public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
+                int userId, Intent[] intents, Bundle bOptions) {
             Objects.requireNonNull(intents, "intents");
             final String[] resolvedTypes = new String[intents.length];
 
@@ -6206,7 +6218,7 @@
             }
 
             return getActivityStartController().startActivitiesInPackage(
-                    packageUid, packageName,
+                    packageUid, packageName, featureId,
                     intents, resolvedTypes, null /* resultTo */,
                     SafeActivityOptions.fromBundle(bOptions), userId,
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
@@ -6215,41 +6227,41 @@
 
         @Override
         public int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-                String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-                SafeActivityOptions options, int userId, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent,
+                String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
+                String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
                 boolean allowBackgroundActivityStart) {
             assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
-                        realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options,
-                        userId, validateIncomingUser, originatingPendingIntent,
+                        realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
+                        resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
                         allowBackgroundActivityStart);
             }
         }
 
         @Override
         public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-                String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-                String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-                int userId, Task inTask, String reason, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent,
+                String callingPackage, @Nullable String callingFeatureId, Intent intent,
+                String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+                int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
                 boolean allowBackgroundActivityStart) {
             assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivityInPackage(uid, realCallingPid,
-                        realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
-                        requestCode, startFlags, options, userId, inTask, reason,
-                        validateIncomingUser, originatingPendingIntent,
+                        realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
+                        resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
+                        reason, validateIncomingUser, originatingPendingIntent,
                         allowBackgroundActivityStart);
             }
         }
 
         @Override
-        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
-                Intent intent, Bundle options, int userId) {
+        public int startActivityAsUser(IApplicationThread caller, String callerPackage,
+                @Nullable String callerFeatureId, Intent intent, Bundle options, int userId) {
             return ActivityTaskManagerService.this.startActivityAsUser(
-                    caller, callerPacakge, intent,
+                    caller, callerPackage, callerFeatureId, intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                     null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
                     false /*validateIncomingUser*/);
@@ -6685,12 +6697,12 @@
 
         @Override
         public IIntentSender getIntentSender(int type, String packageName,
-                int callingUid, int userId, IBinder token, String resultWho,
-                int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
-                Bundle bOptions) {
+                @Nullable String featureId, int callingUid, int userId, IBinder token,
+                String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes,
+                int flags, Bundle bOptions) {
             synchronized (mGlobalLock) {
-                return getIntentSenderLocked(type, packageName, callingUid, userId, token,
-                        resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+                return getIntentSenderLocked(type, packageName, featureId, callingUid, userId,
+                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 16a7564..8fa8119 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -125,7 +125,7 @@
     }
 
     @Override
-    public int startActivity(IBinder whoThread, String callingPackage,
+    public int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId,
             Intent intent, String resolvedType, Bundle bOptions) {
         checkCaller();
         mService.assertPackageMatchesCallingUid(callingPackage);
@@ -148,6 +148,7 @@
         return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
                 .setCaller(appThread)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
                 .setUserId(callingUser)
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index a9dc36d..3771b3e 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -42,6 +42,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
@@ -155,6 +156,7 @@
      */
     private int mRecentsUid = -1;
     private ComponentName mRecentsComponent = null;
+    private @Nullable String mFeatureId;
 
     /**
      * Mapping of user id -> whether recent tasks have been loaded for that user.
@@ -418,6 +420,13 @@
     }
 
     /**
+     * @return the featureId for the recents component.
+     */
+    @Nullable String getRecentsComponentFeatureId() {
+        return mFeatureId;
+    }
+
+    /**
      * @return the uid for the recents component.
      */
     int getRecentsComponentUid() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 9770947..b0492be 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -63,6 +63,7 @@
     private final DisplayContent mDefaultDisplay;
     private final Intent mTargetIntent;
     private final ComponentName mRecentsComponent;
+    private final @Nullable String mRecentsFeatureId;
     private final int mRecentsUid;
     private final @Nullable WindowProcessController mCaller;
     private final int mUserId;
@@ -80,8 +81,8 @@
 
     RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
             ActivityStartController activityStartController, WindowManagerService wm,
-            Intent targetIntent, ComponentName recentsComponent, int recentsUid,
-            @Nullable WindowProcessController caller) {
+            Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId,
+            int recentsUid, @Nullable WindowProcessController caller) {
         mService = atm;
         mStackSupervisor = stackSupervisor;
         mDefaultDisplay = mService.mRootWindowContainer.getDefaultDisplay();
@@ -89,6 +90,7 @@
         mWindowManager = wm;
         mTargetIntent = targetIntent;
         mRecentsComponent = recentsComponent;
+        mRecentsFeatureId = recentsFeatureId;
         mRecentsUid = recentsUid;
         mCaller = caller;
         mUserId = atm.getCurrentUserId();
@@ -456,6 +458,7 @@
                 .obtainStarter(mTargetIntent, reason)
                 .setCallingUid(mRecentsUid)
                 .setCallingPackage(mRecentsComponent.getPackageName())
+                .setCallingFeatureId(mRecentsFeatureId)
                 .setActivityOptions(new SafeActivityOptions(options))
                 .setUserId(mUserId)
                 .execute();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b1db9d7..0e500f9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -183,6 +183,7 @@
     private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
     private static final String ATTR_CALLING_UID = "calling_uid";
     private static final String ATTR_CALLING_PACKAGE = "calling_package";
+    private static final String ATTR_CALLING_FEATURE_ID = "calling_feature_id";
     private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
     private static final String ATTR_RESIZE_MODE = "resize_mode";
     private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
@@ -296,6 +297,7 @@
     // For relaunching the task from recents as though it was launched by the original launcher.
     int mCallingUid;
     String mCallingPackage;
+    String mCallingFeatureId;
 
     private final Rect mTmpStableBounds = new Rect();
     private final Rect mTmpNonDecorBounds = new Rect();
@@ -477,8 +479,8 @@
                 true /*neverRelinquishIdentity*/,
                 _taskDescription != null ? _taskDescription : new TaskDescription(),
                 _taskId, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
-                info.applicationInfo.uid, info.packageName, info.resizeMode,
-                info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
+                info.applicationInfo.uid, info.packageName, null /* default featureId */,
+                info.resizeMode, info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
                 false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
                 _voiceSession, _voiceInteractor, stack);
     }
@@ -500,18 +502,17 @@
     }
 
     /** Don't use constructor directly. This is only used by XML parser. */
-    Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
-            Intent _affinityIntent, String _affinity, String _rootAffinity,
-            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
-            boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
-            int _effectiveUid, String _lastDescription,
+    Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent,
+            String _affinity, String _rootAffinity, ComponentName _realActivity,
+            ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
+            boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription,
             long lastTimeMoved, boolean neverRelinquishIdentity,
             TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
-            boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
-            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
-            ActivityStack stack) {
+            @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
+            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
+            ActivityInfo info, IVoiceInteractionSession _voiceSession,
+            IVoiceInteractor _voiceInteractor, ActivityStack stack) {
         super(atmService.mWindowManager);
 
         EventLogTags.writeWmTaskCreated(_taskId, stack != null ? getRootTaskId() : INVALID_TASK_ID);
@@ -550,6 +551,7 @@
         mNextAffiliateTaskId = nextTaskId;
         mCallingUid = callingUid;
         mCallingPackage = callingPackage;
+        mCallingFeatureId = callingFeatureId;
         mResizeMode = resizeMode;
         if (info != null) {
             setIntent(_intent, info);
@@ -896,6 +898,7 @@
     void setIntent(ActivityRecord r) {
         mCallingUid = r.launchedFromUid;
         mCallingPackage = r.launchedFromPackage;
+        mCallingFeatureId = r.launchedFromFeatureId;
         setIntent(r.intent, r.info);
         setLockTaskAuth(r);
 
@@ -1351,6 +1354,7 @@
             isPersistable = r.isPersistable();
             mCallingUid = r.launchedFromUid;
             mCallingPackage = r.launchedFromPackage;
+            mCallingFeatureId = r.launchedFromFeatureId;
             // Clamp to [1, max].
             maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
                     ActivityTaskManager.getMaxAppRecentsLimitStatic());
@@ -3231,6 +3235,7 @@
         pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
         pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
         pw.print(" mCallingPackage="); pw.println(mCallingPackage);
+        pw.print(" mCallingFeatureId="); pw.println(mCallingFeatureId);
         if (affinity != null || rootAffinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.print(affinity);
             if (affinity == null || !affinity.equals(rootAffinity)) {
@@ -3440,6 +3445,8 @@
         out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
         out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
+        out.attribute(null, ATTR_CALLING_FEATURE_ID,
+                mCallingFeatureId == null ? "" : mCallingFeatureId);
         out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
         out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
                 String.valueOf(mSupportsPictureInPicture));
@@ -3553,16 +3560,17 @@
                 long lastTimeMoved, boolean neverRelinquishIdentity,
                 TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
                 int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-                int resizeMode, boolean supportsPictureInPicture, boolean realActivitySuspended,
+                @Nullable String callingFeatureId, int resizeMode,
+                boolean supportsPictureInPicture, boolean realActivitySuspended,
                 boolean userSetupComplete, int minWidth, int minHeight, ActivityStack stack) {
             return new ActivityStack(service, taskId, intent, affinityIntent, affinity,
                     rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
                     askedCompatMode, userId, effectiveUid, lastDescription,
                     lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
                     prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
-                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
-                    minWidth, minHeight, null /*ActivityInfo*/, null /*_voiceSession*/,
-                    null /*_voiceInteractor*/, stack);
+                    callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                    userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/,
+                    null /*_voiceSession*/, null /*_voiceInteractor*/, stack);
         }
 
         Task restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
@@ -3595,6 +3603,7 @@
             int nextTaskId = INVALID_TASK_ID;
             int callingUid = -1;
             String callingPackage = "";
+            String callingFeatureId = null;
             int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
             boolean supportsPictureInPicture = false;
             Rect lastNonFullscreenBounds = null;
@@ -3675,6 +3684,9 @@
                     case ATTR_CALLING_PACKAGE:
                         callingPackage = attrValue;
                         break;
+                    case ATTR_CALLING_FEATURE_ID:
+                        callingFeatureId = attrValue;
+                        break;
                     case ATTR_RESIZE_MODE:
                         resizeMode = Integer.parseInt(attrValue);
                         break;
@@ -3776,8 +3788,8 @@
                     autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
                     lastTimeOnTop, neverRelinquishIdentity, taskDescription,
                     taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                    userSetupComplete, minWidth, minHeight, null /*stack*/);
+                    callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
+                    realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
             task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
             task.setBounds(lastNonFullscreenBounds);
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 8bbb0d7..b5892b9 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -20,6 +20,11 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
@@ -29,7 +34,6 @@
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.IActivityTaskManager;
 import android.graphics.Point;
@@ -55,11 +59,10 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
 import com.android.server.protolog.common.ProtoLog;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 class TaskPositioner implements IBinder.DeathRecipient {
     private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
     private static final String TAG_LOCAL = "TaskPositioner";
@@ -67,33 +70,10 @@
 
     private static Factory sFactory;
 
-    @IntDef(flag = true,
-            value = {
-                    CTRL_NONE,
-                    CTRL_LEFT,
-                    CTRL_RIGHT,
-                    CTRL_TOP,
-                    CTRL_BOTTOM
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface CtrlType {}
-
-    private static final int CTRL_NONE   = 0x0;
-    private static final int CTRL_LEFT   = 0x1;
-    private static final int CTRL_RIGHT  = 0x2;
-    private static final int CTRL_TOP    = 0x4;
-    private static final int CTRL_BOTTOM = 0x8;
-
     public static final float RESIZING_HINT_ALPHA = 0.5f;
 
     public static final int RESIZING_HINT_DURATION_MS = 0;
 
-    // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
-    // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
-    // aspect he desires.
-    @VisibleForTesting
-    static final float MIN_ASPECT = 1.2f;
-
     private final WindowManagerService mService;
     private final IActivityTaskManager mActivityManager;
     private WindowPositionerEventReceiver mInputEventReceiver;
@@ -477,122 +457,13 @@
      */
     @VisibleForTesting
     void resizeDrag(float x, float y) {
-        // This is a resizing operation.
-        // We need to keep various constraints:
-        // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
-        // 2. The orientation is kept - if required.
-        final int deltaX = Math.round(x - mStartDragX);
-        final int deltaY = Math.round(y - mStartDragY);
-        int left = mWindowOriginalBounds.left;
-        int top = mWindowOriginalBounds.top;
-        int right = mWindowOriginalBounds.right;
-        int bottom = mWindowOriginalBounds.bottom;
-
-        // Calculate the resulting width and height of the drag operation.
-        int width = right - left;
-        int height = bottom - top;
-        if ((mCtrlType & CTRL_LEFT) != 0) {
-            width = Math.max(mMinVisibleWidth, width - deltaX);
-        } else if ((mCtrlType & CTRL_RIGHT) != 0) {
-            width = Math.max(mMinVisibleWidth, width + deltaX);
-        }
-        if ((mCtrlType & CTRL_TOP) != 0) {
-            height = Math.max(mMinVisibleHeight, height - deltaY);
-        } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
-            height = Math.max(mMinVisibleHeight, height + deltaY);
-        }
-
-        // If we have to preserve the orientation - check that we are doing so.
-        final float aspect = (float) width / (float) height;
-        if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
-                || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
-            // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
-            // drag axis. What ever is producing the bigger rectangle will be chosen.
-            int width1;
-            int width2;
-            int height1;
-            int height2;
-            if (mStartOrientationWasLandscape) {
-                // Assuming that the width is our target we calculate the height.
-                width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
-                height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
-                if (height1 < mMinVisibleHeight) {
-                    // If the resulting height is too small we adjust to the minimal size.
-                    height1 = mMinVisibleHeight;
-                    width1 = Math.max(mMinVisibleWidth,
-                            Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
-                }
-                // Assuming that the height is our target we calculate the width.
-                height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
-                width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
-                if (width2 < mMinVisibleWidth) {
-                    // If the resulting width is too small we adjust to the minimal size.
-                    width2 = mMinVisibleWidth;
-                    height2 = Math.max(mMinVisibleHeight,
-                            Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
-                }
-            } else {
-                // Assuming that the width is our target we calculate the height.
-                width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
-                height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
-                if (height1 < mMinVisibleHeight) {
-                    // If the resulting height is too small we adjust to the minimal size.
-                    height1 = mMinVisibleHeight;
-                    width1 = Math.max(mMinVisibleWidth,
-                            Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
-                }
-                // Assuming that the height is our target we calculate the width.
-                height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
-                width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
-                if (width2 < mMinVisibleWidth) {
-                    // If the resulting width is too small we adjust to the minimal size.
-                    width2 = mMinVisibleWidth;
-                    height2 = Math.max(mMinVisibleHeight,
-                            Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
-                }
-            }
-
-            // Use the bigger of the two rectangles if the major change was positive, otherwise
-            // do the opposite.
-            final boolean grows = width > (right - left) || height > (bottom - top);
-            if (grows == (width1 * height1 > width2 * height2)) {
-                width = width1;
-                height = height1;
-            } else {
-                width = width2;
-                height = height2;
-            }
-        }
-
-        // Update mWindowDragBounds to the new drag size.
-        updateDraggedBounds(left, top, right, bottom, width, height);
+        updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
+                mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
+                mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
     }
 
-    /**
-     * Given the old coordinates and the new width and height, update the mWindowDragBounds.
-     *
-     * @param left      The original left bound before the user started dragging.
-     * @param top       The original top bound before the user started dragging.
-     * @param right     The original right bound before the user started dragging.
-     * @param bottom    The original bottom bound before the user started dragging.
-     * @param newWidth  The new dragged width.
-     * @param newHeight The new dragged height.
-     */
-    void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
-                             int newHeight) {
-        // Generate the final bounds by keeping the opposite drag edge constant.
-        if ((mCtrlType & CTRL_LEFT) != 0) {
-            left = right - newWidth;
-        } else { // Note: The right might have changed - if we pulled at the right or not.
-            right = left + newWidth;
-        }
-        if ((mCtrlType & CTRL_TOP) != 0) {
-            top = bottom - newHeight;
-        } else { // Note: The height might have changed - if we pulled at the bottom or not.
-            bottom = top + newHeight;
-        }
-
-        mWindowDragBounds.set(left, top, right, bottom);
+    private void updateDraggedBounds(Rect newBounds) {
+        mWindowDragBounds.set(newBounds);
 
         checkBoundsForOrientationViolations(mWindowDragBounds);
     }
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index add11d6..369db05 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -60,10 +60,11 @@
                 System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
                 new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
                 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
-                RESIZE_MODE_RESIZEABLE, false /*supportsPictureInPicture*/,
-                false /*_realActivitySuspended*/, false /*userSetupComplete*/, INVALID_MIN_SIZE,
-                INVALID_MIN_SIZE, createEmptyActivityInfo(), null /*voiceSession*/,
-                null /*voiceInteractor*/, null /*stack*/);
+                null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
+                false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
+                false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
+                createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
+                null /*stack*/);
         getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
     }
 
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 812bc43..49c7e0a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -13,7 +13,6 @@
     ],
 
     srcs: [
-        ":graphicsstats_proto",
         ":lib_alarmManagerService_native",
         "BroadcastRadio/JavaRef.cpp",
         "BroadcastRadio/NativeCallbackThread.cpp",
@@ -53,7 +52,6 @@
         "com_android_server_UsbHostManager.cpp",
         "com_android_server_VibratorService.cpp",
         "com_android_server_PersistentDataBlockService.cpp",
-        "com_android_server_GraphicsStatsService.cpp",
         "com_android_server_am_CachedAppOptimizer.cpp",
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_incremental_IncrementalManagerService.cpp",
@@ -107,8 +105,6 @@
         "libinputflinger",
         "libinputflinger_base",
         "libinputservice",
-        "libprotobuf-cpp-lite",
-        "libprotoutil",
         "libstatshidl",
         "libstatspull",
         "libstatssocket",
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
deleted file mode 100644
index aa7067e..0000000
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "GraphicsStatsService"
-
-#include <jni.h>
-#include <log/log.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <JankTracker.h>
-#include <service/GraphicsStatsService.h>
-#include <stats_pull_atom_callback.h>
-#include <stats_event.h>
-#include <statslog.h>
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
-#include <android/util/ProtoOutputStream.h>
-#include "android/graphics/Utils.h"
-#include "core_jni_helpers.h"
-#include "protos/graphicsstats.pb.h"
-#include <cstring>
-#include <memory>
-
-namespace android {
-
-using namespace android::uirenderer;
-
-static jint getAshmemSize(JNIEnv*, jobject) {
-    return sizeof(ProfileData);
-}
-
-static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
-    GraphicsStatsService::Dump* dump = GraphicsStatsService::createDump(fd, isProto
-            ? GraphicsStatsService::DumpType::Protobuf : GraphicsStatsService::DumpType::Text);
-    return reinterpret_cast<jlong>(dump);
-}
-
-static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
-        jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
-    std::string path;
-    const ProfileData* data = nullptr;
-    LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
-    ScopedByteArrayRO buffer{env};
-    if (jdata != nullptr) {
-        buffer.reset(jdata);
-        LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
-                "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
-        data = reinterpret_cast<const ProfileData*>(buffer.get());
-    }
-    if (jpath != nullptr) {
-        ScopedUtfChars pathChars(env, jpath);
-        LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
-        path.assign(pathChars.c_str(), pathChars.size());
-    }
-    ScopedUtfChars packageChars(env, jpackage);
-    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
-
-    const std::string package(packageChars.c_str(), packageChars.size());
-    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
-}
-
-static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
-    ScopedUtfChars pathChars(env, jpath);
-    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
-    const std::string path(pathChars.c_str(), pathChars.size());
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    GraphicsStatsService::addToDump(dump, path);
-}
-
-static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    GraphicsStatsService::finishDump(dump);
-}
-
-static jlong finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr) {
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    std::vector<uint8_t>* result = new std::vector<uint8_t>();
-    GraphicsStatsService::finishDumpInMemory(dump,
-        [](void* buffer, int bufferOffset, int bufferSize, int totalSize, void* param1, void* param2) {
-            std::vector<uint8_t>* outBuffer = reinterpret_cast<std::vector<uint8_t>*>(param2);
-            if (outBuffer->size() < totalSize) {
-                outBuffer->resize(totalSize);
-            }
-            std::memcpy(outBuffer->data() + bufferOffset, buffer, bufferSize);
-        }, env, result);
-    return reinterpret_cast<jlong>(result);
-}
-
-static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
-        jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
-    ScopedByteArrayRO buffer(env, jdata);
-    LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
-            "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
-    ScopedUtfChars pathChars(env, jpath);
-    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
-    ScopedUtfChars packageChars(env, jpackage);
-    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
-
-    const std::string path(pathChars.c_str(), pathChars.size());
-    const std::string package(packageChars.c_str(), packageChars.size());
-    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
-    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
-}
-
-static jobject gGraphicsStatsServiceObject = nullptr;
-static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
-
-static JNIEnv* getJNIEnv() {
-    JavaVM* vm = AndroidRuntime::getJavaVM();
-    JNIEnv* env = nullptr;
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
-            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
-        }
-    }
-    return env;
-}
-
-using namespace google::protobuf;
-
-// Field ids taken from FrameTimingHistogram message in atoms.proto
-#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
-#define FRAME_COUNTS_FIELD_NUMBER 2
-
-static void writeCpuHistogram(AStatsEvent* event,
-                              const uirenderer::protos::GraphicsStatsProto& stat) {
-    util::ProtoOutputStream proto;
-    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
-        auto& bucket = stat.histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
-                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
-                    (int)bucket.render_millis());
-    }
-    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
-        auto& bucket = stat.histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
-                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
-                    (long long)bucket.frame_count());
-    }
-    std::vector<uint8_t> outVector;
-    proto.serializeToVector(&outVector);
-    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
-}
-
-static void writeGpuHistogram(AStatsEvent* event,
-                              const uirenderer::protos::GraphicsStatsProto& stat) {
-    util::ProtoOutputStream proto;
-    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
-        auto& bucket = stat.gpu_histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
-                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
-                    (int)bucket.render_millis());
-    }
-    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
-        auto& bucket = stat.gpu_histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
-                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
-                    (long long)bucket.frame_count());
-    }
-    std::vector<uint8_t> outVector;
-    proto.serializeToVector(&outVector);
-    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
-}
-
-// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
-                                                                      AStatsEventList* data,
-                                                                      void* cookie) {
-    JNIEnv* env = getJNIEnv();
-    if (!env) {
-        return false;
-    }
-    if (gGraphicsStatsServiceObject == nullptr) {
-        ALOGE("Failed to get graphicsstats service");
-        return AStatsManager_PULL_SKIP;
-    }
-
-    for (bool lastFullDay : {true, false}) {
-        jlong jdata = (jlong) env->CallLongMethod(
-                    gGraphicsStatsServiceObject,
-                    gGraphicsStatsService_pullGraphicsStatsMethodID,
-                    (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE));
-        if (env->ExceptionCheck()) {
-            env->ExceptionDescribe();
-            env->ExceptionClear();
-            ALOGE("Failed to invoke graphicsstats service");
-            return AStatsManager_PULL_SKIP;
-        }
-        if (!jdata) {
-            // null means data is not available for that day.
-            continue;
-        }
-        android::uirenderer::protos::GraphicsStatsServiceDumpProto serviceDump;
-        std::vector<uint8_t>* buffer = reinterpret_cast<std::vector<uint8_t>*>(jdata);
-        std::unique_ptr<std::vector<uint8_t>> bufferRelease(buffer);
-        int dataSize = buffer->size();
-        if (!dataSize) {
-            // Data is not available for that day.
-            continue;
-        }
-        io::ArrayInputStream input{buffer->data(), dataSize};
-        bool success = serviceDump.ParseFromZeroCopyStream(&input);
-        if (!success) {
-            ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
-                  serviceDump.InitializationErrorString().c_str(), dataSize);
-            return AStatsManager_PULL_SKIP;
-        }
-
-        for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
-            auto& stat = serviceDump.stats(stat_index);
-            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
-            AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
-            AStatsEvent_writeString(event, stat.package_name().c_str());
-            AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
-            AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
-            AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
-            AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
-            writeCpuHistogram(event, stat);
-            writeGpuHistogram(event, stat);
-            // TODO: fill in UI mainline module version, when the feature is available.
-            AStatsEvent_writeInt64(event, (int64_t)0);
-            AStatsEvent_writeBool(event, !lastFullDay);
-            AStatsEvent_build(event);
-        }
-    }
-    return AStatsManager_PULL_SUCCESS;
-}
-
-// Register a puller for GRAPHICS_STATS atom with the statsd service.
-static void nativeInit(JNIEnv* env, jobject javaObject) {
-    gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000);  // 10 milliseconds
-    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
-
-    AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
-                                           &graphicsStatsPullCallback, metadata, nullptr);
-
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-static void nativeDestructor(JNIEnv* env, jobject javaObject) {
-    AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
-    env->DeleteGlobalRef(gGraphicsStatsServiceObject);
-    gGraphicsStatsServiceObject = nullptr;
-}
-
-static const JNINativeMethod sMethods[] = {
-    { "nGetAshmemSize", "()I", (void*) getAshmemSize },
-    { "nCreateDump", "(IZ)J", (void*) createDump },
-    { "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) addToDump },
-    { "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
-    { "nFinishDump", "(J)V", (void*) finishDump },
-    { "nFinishDumpInMemory", "(J)J", (void*) finishDumpInMemory },
-    { "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) saveBuffer },
-    { "nativeInit", "()V", (void*) nativeInit },
-    { "nativeDestructor",   "()V",     (void*)nativeDestructor }
-};
-
-int register_android_server_GraphicsStatsService(JNIEnv* env)
-{
-    jclass graphicsStatsService_class = FindClassOrDie(env,
-            "com/android/server/GraphicsStatsService");
-    gGraphicsStatsService_pullGraphicsStatsMethodID = GetMethodIDOrDie(env,
-            graphicsStatsService_class, "pullGraphicsStats", "(Z)J");
-    return jniRegisterNativeMethods(env, "com/android/server/GraphicsStatsService",
-                                    sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 1202ad3..c186494 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -49,7 +49,7 @@
 int register_android_server_Watchdog(JNIEnv* env);
 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
 int register_android_server_SyntheticPasswordManager(JNIEnv* env);
-int register_android_server_GraphicsStatsService(JNIEnv* env);
+int register_android_graphics_GraphicsStatsService(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
 int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
@@ -102,7 +102,7 @@
     register_android_server_HardwarePropertiesManagerService(env);
     register_android_server_storage_AppFuse(env);
     register_android_server_SyntheticPasswordManager(env);
-    register_android_server_GraphicsStatsService(env);
+    register_android_graphics_GraphicsStatsService(env);
     register_android_hardware_display_DisplayViewport(env);
     register_android_server_net_NetworkStatsFactory(env);
     register_android_server_net_NetworkStatsService(env);
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 0941831..b2c316a 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -16,6 +16,7 @@
 
 #include "BinderIncrementalService.h"
 
+#include <android-base/logging.h>
 #include <binder/IResultReceiver.h>
 #include <binder/PermissionCache.h>
 #include <incfs.h>
@@ -24,7 +25,6 @@
 #include "jni.h"
 #include "nativehelper/JNIHelp.h"
 #include "path.h"
-#include <android-base/logging.h>
 
 using namespace std::literals;
 using namespace android::incremental;
@@ -277,6 +277,13 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::configureNativeBinaries(
+        int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath,
+        const std::string& abi, bool* _aidl_return) {
+    *_aidl_return = mImpl.configureNativeBinaries(storageId, apkFullPath, libDirRelativePath, abi);
+    return ok();
+}
+
 } // namespace android::os::incremental
 
 jlong Incremental_IncrementalService_Start() {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 8a09977..51d7de3 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -28,11 +28,11 @@
 class BinderIncrementalService : public BnIncrementalService,
                                  public BinderService<BinderIncrementalService> {
 public:
-    BinderIncrementalService(const sp<IServiceManager> &sm);
+    BinderIncrementalService(const sp<IServiceManager>& sm);
 
-    static BinderIncrementalService *start();
-    static const char16_t *getServiceName() { return u"incremental_service"; }
-    status_t dump(int fd, const Vector<String16> &args) final;
+    static BinderIncrementalService* start();
+    static const char16_t* getServiceName() { return u"incremental_service"; }
+    status_t dump(int fd, const Vector<String16>& args) final;
 
     void onSystemReady();
     void onInvalidStorage(int mountId);
@@ -70,6 +70,9 @@
                                    std::vector<uint8_t>* _aidl_return) final;
     binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
     binder::Status deleteStorage(int32_t storageId) final;
+    binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
+                                           const std::string& libDirRelativePath,
+                                           const std::string& abi, bool* _aidl_return) final;
 
 private:
     android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index dbd97cf..3b51377 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -60,6 +60,9 @@
     static constexpr auto storagePrefix = "st"sv;
     static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
     static constexpr auto infoMdName = ".info"sv;
+    static constexpr auto libDir = "lib"sv;
+    static constexpr auto libSuffix = ".so"sv;
+    static constexpr auto blockSize = 4096;
 };
 
 static const Constants& constants() {
@@ -259,16 +262,18 @@
 
 inline const char* toString(TimePoint t) {
     using SystemClock = std::chrono::system_clock;
-    time_t time = SystemClock::to_time_t(SystemClock::now() + std::chrono::duration_cast<SystemClock::duration>(t - Clock::now()));
+    time_t time = SystemClock::to_time_t(
+            SystemClock::now() +
+            std::chrono::duration_cast<SystemClock::duration>(t - Clock::now()));
     return std::ctime(&time);
 }
 
 inline const char* toString(IncrementalService::BindKind kind) {
     switch (kind) {
-    case IncrementalService::BindKind::Temporary:
-        return "Temporary";
-    case IncrementalService::BindKind::Permanent:
-        return "Permanent";
+        case IncrementalService::BindKind::Temporary:
+            return "Temporary";
+        case IncrementalService::BindKind::Permanent:
+            return "Permanent";
     }
 }
 
@@ -1124,6 +1129,122 @@
     return true;
 }
 
+// Extract lib filse from zip, create new files in incfs and write data to them
+bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
+                                                 std::string_view libDirRelativePath,
+                                                 std::string_view abi) {
+    const auto ifs = getIfs(storage);
+    // First prepare target directories if they don't exist yet
+    if (auto res = makeDirs(storage, libDirRelativePath, 0755)) {
+        LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
+                   << " errno: " << res;
+        return false;
+    }
+
+    std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data()));
+    if (!zipFile) {
+        LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
+        return false;
+    }
+    void* cookie = nullptr;
+    const auto libFilePrefix = path::join(constants().libDir, abi);
+    if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
+                                       constants().libSuffix.data() /* suffix */)) {
+        LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
+        return false;
+    }
+    ZipEntryRO entry = nullptr;
+    bool success = true;
+    while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) {
+        char fileName[PATH_MAX];
+        if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) {
+            continue;
+        }
+        const auto libName = path::basename(fileName);
+        const auto targetLibPath = path::join(libDirRelativePath, libName);
+        const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
+        // If the extract file already exists, skip
+        struct stat st;
+        if (stat(targetLibPathAbsolute.c_str(), &st) == 0) {
+            LOG(INFO) << "Native lib file already exists: " << targetLibPath
+                      << "; skipping extraction";
+            continue;
+        }
+
+        uint32_t uncompressedLen;
+        if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr,
+                                         nullptr, nullptr)) {
+            LOG(ERROR) << "Failed to read native lib entry: " << fileName;
+            success = false;
+            break;
+        }
+
+        // Create new lib file without signature info
+        incfs::NewFileParams libFileParams;
+        libFileParams.size = uncompressedLen;
+        libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE;
+        // Metadata of the new lib file is its relative path
+        IncFsSpan libFileMetadata;
+        libFileMetadata.data = targetLibPath.c_str();
+        libFileMetadata.size = targetLibPath.size();
+        libFileParams.metadata = libFileMetadata;
+        incfs::FileId libFileId = idFromMetadata(targetLibPath);
+        if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) {
+            LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
+            success = false;
+            // If one lib file fails to be created, abort others as well
+            break;
+        }
+
+        // Write extracted data to new file
+        std::vector<uint8_t> libData(uncompressedLen);
+        if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) {
+            LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName;
+            success = false;
+            break;
+        }
+        android::base::unique_fd writeFd(mIncFs->openWrite(ifs->control, libFileId));
+        if (writeFd < 0) {
+            LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
+            success = false;
+            break;
+        }
+        const int numBlocks = uncompressedLen / constants().blockSize + 1;
+        std::vector<IncFsDataBlock> instructions;
+        auto remainingData = std::span(libData);
+        for (int i = 0; i < numBlocks - 1; i++) {
+            auto inst = IncFsDataBlock{
+                    .fileFd = writeFd,
+                    .pageIndex = static_cast<IncFsBlockIndex>(i),
+                    .compression = INCFS_COMPRESSION_KIND_NONE,
+                    .kind = INCFS_BLOCK_KIND_DATA,
+                    .dataSize = static_cast<uint16_t>(constants().blockSize),
+                    .data = reinterpret_cast<const char*>(remainingData.data()),
+            };
+            instructions.push_back(inst);
+            remainingData = remainingData.subspan(constants().blockSize);
+        }
+        // Last block
+        auto inst = IncFsDataBlock{
+                .fileFd = writeFd,
+                .pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1),
+                .compression = INCFS_COMPRESSION_KIND_NONE,
+                .kind = INCFS_BLOCK_KIND_DATA,
+                .dataSize = static_cast<uint16_t>(remainingData.size()),
+                .data = reinterpret_cast<const char*>(remainingData.data()),
+        };
+        instructions.push_back(inst);
+        size_t res = mIncFs->writeBlocks(instructions);
+        if (res != instructions.size()) {
+            LOG(ERROR) << "Failed to write data into: " << targetLibPath;
+            success = false;
+        }
+        instructions.clear();
+    }
+    zipFile.get()->endIteration(cookie);
+    return success;
+}
+
 binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
                                                                                   int newStatus) {
     std::unique_lock l(incrementalService.mLock);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index dec9f64..2444dde 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -126,7 +126,8 @@
 
     std::vector<std::string> listFiles(StorageId storage) const;
     bool startLoading(StorageId storage) const;
-
+    bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
+                                 std::string_view libDirRelativePath, std::string_view abi);
     class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
     public:
         IncrementalDataLoaderListener(IncrementalService& incrementalService)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 036335c..eef6c63 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
+import android.graphics.GraphicsStatsService;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.ConnectivityModuleConnector;
 import android.net.ITetheringConnector;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 3975f0b..e3453a0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -50,6 +50,7 @@
 @RunWith(AndroidJUnit4.class)
 public class PendingIntentControllerTest {
     private static final String TEST_PACKAGE_NAME = "test-package-1";
+    private static final String TEST_FEATURE_ID = "test-feature-1";
     private static final int TEST_CALLING_UID = android.os.Process.myUid();
     private static final Intent[] TEST_INTENTS = new Intent[]{new Intent("com.test.intent")};
 
@@ -87,8 +88,8 @@
 
     private PendingIntentRecord createPendingIntentRecord(int flags) {
         return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST,
-                TEST_PACKAGE_NAME, TEST_CALLING_UID, 0, null, null, 0, TEST_INTENTS, null, flags,
-                null);
+                TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, 0, null, null, 0,
+                TEST_INTENTS, null, flags, null);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 25964c5..3778e17 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -43,7 +43,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -210,7 +209,6 @@
         verify(file3, never()).delete();
     }
 
-    @Ignore
     @Test
     public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception {
         // Setup sessions
@@ -218,8 +216,8 @@
         doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS + 1000)
                 .when(sessionFile1).lastModified();
         final long sessionId1 = 342;
-        final BlobHandle blobHandle1 = mock(BlobHandle.class);
-        doReturn(System.currentTimeMillis() - 1000).when(blobHandle1).getExpiryTimeMillis();
+        final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+                "label1", System.currentTimeMillis() - 1000, "tag1");
         final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
                 sessionId1, sessionFile1, blobHandle1);
         mUserSessions.append(sessionId1, session1);
@@ -228,8 +226,8 @@
         doReturn(System.currentTimeMillis() - 20000)
                 .when(sessionFile2).lastModified();
         final long sessionId2 = 4597;
-        final BlobHandle blobHandle2 = mock(BlobHandle.class);
-        doReturn(System.currentTimeMillis() + 20000).when(blobHandle2).getExpiryTimeMillis();
+        final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+                "label2", System.currentTimeMillis() + 20000, "tag2");
         final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
                 sessionId2, sessionFile2, blobHandle2);
         mUserSessions.append(sessionId2, session2);
@@ -238,8 +236,8 @@
         doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS - 2000)
                 .when(sessionFile3).lastModified();
         final long sessionId3 = 9484;
-        final BlobHandle blobHandle3 = mock(BlobHandle.class);
-        doReturn(System.currentTimeMillis() + 30000).when(blobHandle3).getExpiryTimeMillis();
+        final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
+                "label3", System.currentTimeMillis() + 30000, "tag3");
         final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
                 sessionId3, sessionFile3, blobHandle3);
         mUserSessions.append(sessionId3, session3);
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 089a79b..a871ec6 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -178,6 +178,7 @@
                 new Intent(),
                 null /* callerApp */,
                 null  /* callerPackage */,
+                null /* callerFeatureId */,
                 0 /* callingPid */,
                 0 /* callingUid */,
                 false /* callerInstantApp */,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 3d190be..df2b3ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -24,6 +24,7 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -556,6 +557,11 @@
         void injectRestoreCallingIdentity(long token) {
             mInjectedCallingUid = (int) token;
         }
+
+        @Override
+        boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
+            return true;
+        }
     }
 
     protected class LauncherAppsTestable extends LauncherApps {
@@ -1617,6 +1623,22 @@
     }
 
     /**
+     * Make a long lived shortcut with an ID.
+     */
+    protected ShortcutInfo makeLongLivedShortcut(String id) {
+        final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, id)
+                .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
+                .setShortLabel("title-" + id)
+                .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class))
+                .setLongLived(true);
+        final ShortcutInfo s = b.build();
+
+        s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+        return s;
+    }
+
+    /**
      * Make an intent.
      */
     protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
@@ -1707,6 +1729,7 @@
         final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
         verify(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
                 eq(packageName),
+                isNull(),
                 eq(userId),
                 intentsCaptor.capture(),
                 anyOrNull(Bundle.class));
@@ -1765,6 +1788,7 @@
         // This shouldn't have been called.
         verify(mMockActivityTaskManagerInternal, times(0)).startActivitiesAsPackage(
                 anyString(),
+                isNull(),
                 anyInt(),
                 any(Intent[].class),
                 anyOrNull(Bundle.class));
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index 91cc9f3..fea1b82 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -55,6 +55,7 @@
 @RunWith(MockitoJUnitRunner.class)
 public class CrossProfileAppsServiceImplTest {
     private static final String PACKAGE_ONE = "com.one";
+    private static final String FEATURE_ID = "feature.one";
     private static final int PACKAGE_ONE_UID = 1111;
     private static final ComponentName ACTIVITY_COMPONENT =
             new ComponentName("com.one", "test");
@@ -220,6 +221,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                                 true));
@@ -228,6 +230,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -241,6 +244,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                                 false));
@@ -249,6 +253,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -264,6 +269,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -272,6 +278,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -287,6 +294,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -295,6 +303,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -308,6 +317,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_TWO,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -316,6 +326,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -329,6 +340,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_TWO,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -337,6 +349,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -352,6 +365,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -360,6 +374,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -380,6 +395,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -388,6 +404,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -401,6 +418,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -409,6 +427,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -422,6 +441,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -430,6 +450,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -443,6 +464,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER).getIdentifier(),
                                 true));
@@ -451,6 +473,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -464,6 +487,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER).getIdentifier(),
                                 false));
@@ -472,6 +496,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -484,6 +509,7 @@
         mCrossProfileAppsServiceImpl.startActivityAsUser(
                 mIApplicationThread,
                 PACKAGE_ONE,
+                FEATURE_ID,
                 ACTIVITY_COMPONENT,
                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                 true);
@@ -492,6 +518,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         eq(PACKAGE_ONE),
+                        eq(FEATURE_ID),
                         any(Intent.class),
                         nullable(Bundle.class),
                         eq(PRIMARY_USER));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 63da5fb..2936bdd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -1240,7 +1240,7 @@
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+                    makeLongLivedShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
         });
 
         // Pin 2 and 3
@@ -1250,9 +1250,12 @@
         });
 
         // Cache 1 and 2
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"),
+                    HANDLE_USER_0);
+        });
+
         setCaller(CALLING_PACKAGE_1);
-        getCallerShortcut("s1").setCached();
-        getCallerShortcut("s2").setCached();
 
         // Get manifest shortcuts
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_MANIFEST),
@@ -1315,8 +1318,9 @@
 
     public void testCachedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeShortcut("s2"),
-                    makeShortcut("s3"), makeShortcut("s4"))));
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+                    makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
+                    makeLongLivedShortcut("s4"))));
         });
 
         // Pin s2
@@ -1325,11 +1329,13 @@
                     HANDLE_USER_0);
         });
 
-        // Cache 2, 3 and 4
+        // Cache some, but non long lived shortcuts will be ignored.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s4"),
+                    HANDLE_USER_0);
+        });
+
         setCaller(CALLING_PACKAGE_1);
-        getCallerShortcut("s2").setCached();
-        getCallerShortcut("s3").setCached();
-        getCallerShortcut("s4").setCached();
 
         // Get dynamic shortcuts
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
@@ -1339,27 +1345,37 @@
                 "s2");
         // Get cached shortcuts
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
-                "s2", "s3", "s4");
+                "s2", "s4");
 
         // Remove a dynamic cached shortcut
-        mManager.removeDynamicShortcuts(list("s3"));
+        mManager.removeDynamicShortcuts(list("s4"));
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
-                "s1", "s2", "s4");
+                "s1", "s2", "s3");
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
-                "s2", "s3", "s4");
+                "s2", "s4");
 
-        // Remove dynamic cached long lived shortcuts
-        mManager.removeLongLivedShortcuts(list("s3", "s4"));
-        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
-                "s1", "s2");
+        // uncache a non-dynamic shortcut. Should be removed.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s4"),
+                    HANDLE_USER_0);
+        });
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
                 "s2");
 
+        // Cache another shortcut
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s3"),
+                    HANDLE_USER_0);
+        });
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+                "s2", "s3");
+
         // Remove a dynamic cached pinned long lived shortcut
         mManager.removeLongLivedShortcuts(list("s2"));
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
-                "s1");
-        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED));
+                "s1", "s3");
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+                "s3");
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_PINNED),
                 "s2");
     }
@@ -1371,7 +1387,7 @@
         // Set up shortcuts.
 
         setCaller(CALLING_PACKAGE_1);
-        final ShortcutInfo s1_1 = makeShortcut("s1");
+        final ShortcutInfo s1_1 = makeLongLivedShortcut("s1");
         final ShortcutInfo s1_2 = makeShortcutWithLocusId("s2", makeLocusId("l1"));
 
         assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
@@ -1395,6 +1411,8 @@
 
         setCaller(CALLING_PACKAGE_3);
         final ShortcutInfo s3_2 = makeShortcutWithLocusId("s3", makeLocusId("l2"));
+        s3_2.setLongLived();
+
         assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
 
         getCallerShortcut("s3").setTimestamp(START_TIME + 5000);
@@ -1535,26 +1553,20 @@
 
         // TODO More tests: pinned but dynamic.
 
-        // Cache some shortcuts
-        setCaller(CALLING_PACKAGE_1);
-        getCallerShortcut("s1").setCached();
-
-        setCaller(CALLING_PACKAGE_2);
-        getCallerShortcut("s4").setCached();
-
-        setCaller(CALLING_PACKAGE_3);
-        getCallerShortcut("s3").setCached();
-
         setCaller(LAUNCHER_1);
 
+        // Cache some shortcuts. Only long lived shortcuts can get cached.
+        mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), getCallingUser());
+        mLauncherApps.cacheShortcuts(CALLING_PACKAGE_3, list("s3"), getCallingUser());
+
         // Cached ones only
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mLauncherApps.getShortcuts(buildQuery(
-                        /* time =*/ 0, CALLING_PACKAGE_2,
+                        /* time =*/ 0, CALLING_PACKAGE_3,
                         /* activity =*/ null,
                         ShortcutQuery.FLAG_MATCH_CACHED),
                         getCallingUser())),
-                "s4");
+                "s3");
 
         // All packages.
         assertShortcutIds(assertAllNotKeyFieldsOnly(
@@ -1563,7 +1575,7 @@
                         /* activity =*/ null,
                         ShortcutQuery.FLAG_MATCH_CACHED),
                         getCallingUser())),
-                "s1", "s4", "s3");
+                "s1", "s3");
 
         assertExpectException(
                 IllegalArgumentException.class, "package name must also be set", () -> {
@@ -1581,7 +1593,7 @@
                         /* activity =*/ null,
                         ShortcutQuery.FLAG_MATCH_CACHED),
                         getCallingUser())),
-                "s1", "s4", "s3");
+                "s1", "s3");
     }
 
     public void testGetShortcuts_shortcutKinds() throws Exception {
@@ -3132,7 +3144,7 @@
             // Not launchable.
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
                     .when(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
-                    anyStringOrNull(), anyInt(),
+                    anyStringOrNull(), anyStringOrNull(), anyInt(),
                     anyOrNull(Intent[].class), anyOrNull(Bundle.class));
             assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
@@ -3141,7 +3153,7 @@
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
                     .when(mMockActivityTaskManagerInternal)
                     .startActivitiesAsPackage(
-                            anyStringOrNull(), anyInt(),
+                            anyStringOrNull(), anyStringOrNull(), anyInt(),
                             anyOrNull(Intent[].class), anyOrNull(Bundle.class));
             assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index fde0ddf..4d0481be 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -144,6 +144,40 @@
         assertEquals("Incorrect blacklist", expectedBlack, actualBlack);
     }
 
+    @Test
+    public void testComponentOverride() throws Exception {
+        final String contents =
+                "<permissions>"
+                + "    <component-override package=\"com.android.package1\">\n"
+                + "        <component class=\"com.android.package1.Full\" enabled=\"true\"/>"
+                + "        <component class=\".Relative\" enabled=\"false\" />\n"
+                + "    </component-override>"
+                + "    <component-override package=\"com.android.package2\" >\n"
+                + "        <component class=\"com.android.package3.Relative2\" enabled=\"yes\" />\n"
+                + "    </component-override>\n"
+                + "</permissions>";
+
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "component-override.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* No permission needed anyway */ 0);
+
+        final ArrayMap<String, Boolean>  packageOneExpected = new ArrayMap<>();
+        packageOneExpected.put("com.android.package1.Full", true);
+        packageOneExpected.put("com.android.package1.Relative", false);
+
+        final ArrayMap<String, Boolean> packageTwoExpected = new ArrayMap<>();
+        packageTwoExpected.put("com.android.package3.Relative2", true);
+
+        final Map<String, Boolean> packageOne = mSysConfig.getComponentsEnabledStates(
+                "com.android.package1");
+        assertEquals(packageOneExpected, packageOne);
+
+        final Map<String, Boolean> packageTwo = mSysConfig.getComponentsEnabledStates(
+                "com.android.package2");
+        assertEquals(packageTwoExpected, packageTwo);
+    }
+
     /**
      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
      * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index 5b5ad87..cbb760a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -44,6 +44,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.internal.matchers.Not;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -239,6 +240,52 @@
         verify(af2, never()).openRead();
     }
 
+    @Test
+    public void testRemoveNotificationRunnable() throws Exception {
+        NotificationHistory nh = mock(NotificationHistory.class);
+        NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
+                mDataBase.new RemoveNotificationRunnable("pkg", 123);
+        rnr.setNotificationHistory(nh);
+
+        AtomicFile af = mock(AtomicFile.class);
+        when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+        mDataBase.mHistoryFiles.addLast(af);
+
+        when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(true);
+
+        mDataBase.mBuffer = mock(NotificationHistory.class);
+
+        rnr.run();
+
+        verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
+        verify(af).openRead();
+        verify(nh).removeNotificationFromWrite("pkg", 123);
+        verify(af).startWrite();
+    }
+
+    @Test
+    public void testRemoveNotificationRunnable_noChanges() throws Exception {
+        NotificationHistory nh = mock(NotificationHistory.class);
+        NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
+                mDataBase.new RemoveNotificationRunnable("pkg", 123);
+        rnr.setNotificationHistory(nh);
+
+        AtomicFile af = mock(AtomicFile.class);
+        when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+        mDataBase.mHistoryFiles.addLast(af);
+
+        when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(false);
+
+        mDataBase.mBuffer = mock(NotificationHistory.class);
+
+        rnr.run();
+
+        verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
+        verify(af).openRead();
+        verify(nh).removeNotificationFromWrite("pkg", 123);
+        verify(af, never()).startWrite();
+    }
+
     private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
         public Map<File, Long> creationDates = new HashMap<>();
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index b5eb1bf..2c548be 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -289,6 +289,20 @@
     }
 
     @Test
+    public void testDeleteNotificationHistoryItem_userUnlocked() {
+        String pkg = "pkg";
+        long time = 235;
+        NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
+
+        mHistoryManager.onUserUnlocked(USER_SYSTEM);
+        mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
+
+        mHistoryManager.deleteNotificationHistoryItem(pkg, 1, time);
+
+        verify(userHistory, times(1)).deleteNotificationHistoryItem(pkg, time);
+    }
+
+    @Test
     public void testTriggerWriteToDisk() {
         NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
         NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e0ee3ce..9ec339d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -42,6 +42,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -128,6 +130,7 @@
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
@@ -6273,4 +6276,60 @@
 
         assertEquals(PRIORITY_CATEGORY_CALLS, actual);
     }
+
+    @Test
+    public void testGetConversationsForPackage_hasShortcut() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+        NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+        channel1.setConversationId("parent1", "convo 1");
+        convo1.setNotificationChannel(channel1);
+        convos.add(convo1);
+
+        ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+        NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+        channel2.setConversationId("parent1", "convo 2");
+        convo2.setNotificationChannel(channel2);
+        convos.add(convo2);
+        when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+        // only one valid shortcut
+        LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+                .setPackage(PKG_P)
+                .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+                .setShortcutIds(Arrays.asList(channel1.getConversationId()));
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.getShortLabel()).thenReturn("Hello");
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+
+        List<ConversationChannelWrapper> conversations =
+                mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+        assertEquals(si, conversations.get(0).getShortcutInfo());
+        assertEquals(si, conversations.get(1).getShortcutInfo());
+
+    }
+
+    @Test
+    public void testGetConversationsForPackage_doesNotHaveShortcut() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+        NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+        channel1.setConversationId("parent1", "convo 1");
+        convo1.setNotificationChannel(channel1);
+        convos.add(convo1);
+
+        ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+        NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+        channel2.setConversationId("parent1", "convo 2");
+        convo2.setNotificationChannel(channel2);
+        convos.add(convo2);
+        when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+        List<ConversationChannelWrapper> conversations =
+                mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+        assertNull(conversations.get(0).getShortcutInfo());
+        assertNull(conversations.get(1).getShortcutInfo());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index bb84b04..3f690b1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -26,6 +26,8 @@
 
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
 
@@ -64,6 +66,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.service.notification.ConversationChannelWrapper;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableContentResolver;
 import android.util.ArrayMap;
@@ -91,6 +94,7 @@
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -2939,4 +2943,97 @@
 
         assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
     }
+
+    @Test
+    public void testGetConversations_invalidPkg() {
+        assertThat(mHelper.getConversations("bad", 1)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations_noConversations() {
+        NotificationChannel channel =
+                new NotificationChannel("not_convo", "not_convo", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations_noDisabledGroups() {
+        NotificationChannelGroup group = new NotificationChannelGroup("a", "a");
+        group.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+        NotificationChannel parent = new NotificationChannel("parent", "p", 1);
+        mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("convo", "convo", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "convo");
+        channel.setGroup(group.getId());
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations_noDeleted() {
+        NotificationChannel parent = new NotificationChannel("parent", "p", 1);
+        mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+        NotificationChannel channel =
+                new NotificationChannel("convo", "convo", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "convo");
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+        mHelper.deleteNotificationChannel(PKG_O, UID_O, channel.getId());
+
+        assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations() {
+        NotificationChannelGroup group = new NotificationChannelGroup("acct", "account_name");
+        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+
+        NotificationChannel messages =
+                new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+        messages.setGroup(group.getId());
+        mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+        NotificationChannel calls =
+                new NotificationChannel("calls", "Calls", IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("A person", "A lovely person", IMPORTANCE_DEFAULT);
+        channel.setGroup(group.getId());
+        channel.setConversationId(messages.getId(), channel.getName().toString());
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("B person", "B fabulous person", IMPORTANCE_DEFAULT);
+        channel2.setConversationId(calls.getId(), channel.getName().toString());
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+        Map<String, NotificationChannel> expected = new HashMap<>();
+        expected.put(channel.getId(), channel);
+        expected.put(channel2.getId(), channel2);
+
+        Map<String, CharSequence> expectedGroup = new HashMap<>();
+        expectedGroup.put(channel.getId(), group.getName());
+        expectedGroup.put(channel2.getId(), null);
+
+        Map<String, CharSequence> expectedParentLabel= new HashMap<>();
+        expectedParentLabel.put(channel.getId(), messages.getName());
+        expectedParentLabel.put(channel2.getId(), calls.getName());
+
+        ArrayList<ConversationChannelWrapper> convos = mHelper.getConversations(PKG_O, UID_O);
+        assertThat(convos).hasSize(2);
+
+        for (ConversationChannelWrapper convo : convos) {
+            assertThat(convo.getNotificationChannel())
+                    .isEqualTo(expected.get(convo.getNotificationChannel().getId()));
+            assertThat(convo.getParentChannelLabel())
+                    .isEqualTo(expectedParentLabel.get(convo.getNotificationChannel().getId()));
+            assertThat(convo.getGroupLabel())
+                    .isEqualTo(expectedGroup.get(convo.getNotificationChannel().getId()));
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 135d005..af04f76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -120,7 +120,7 @@
         mInterceptor = new ActivityStartInterceptor(
                 mService, mSupervisor, mRootWindowContainer, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
-                TEST_START_FLAGS, TEST_CALLING_PACKAGE);
+                TEST_START_FLAGS, TEST_CALLING_PACKAGE, null);
 
         // Mock ActivityManagerInternal
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index bab877e..fa182d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -275,7 +275,7 @@
 
         if (containsConditions(preconditions, PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
             doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
-                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(),
                     anyBoolean(), anyBoolean(), any(), any(), any());
         }
 
@@ -349,7 +349,7 @@
             boolean mockGetLaunchStack) {
         // always allow test to start activity.
         doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
-                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
         if (mockGetLaunchStack) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index eb84d0a..c93dc97 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -255,10 +255,10 @@
 
             final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
                     mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */,
-                    null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */,
-                    null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/,
-                    false /* rootVoiceInteraction */, mService.mStackSupervisor, options,
-                    null /* sourceRecord */);
+                    null, null, intent, null, aInfo /*aInfo*/, new Configuration(),
+                    null /* resultTo */, null /* resultWho */, 0 /* reqCode */,
+                    false /*componentSpecified*/, false /* rootVoiceInteraction */,
+                    mService.mStackSupervisor, options, null /* sourceRecord */);
             spyOn(activity);
             if (mTask != null) {
                 // fullscreen value is normally read from resources in ctor, so for testing we need
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index e507508..f517881 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -439,11 +439,11 @@
             doNothing().when(this).updateCpuStats();
 
             // AppOpsService
-            final AppOpsService aos = mock(AppOpsService.class);
-            doReturn(aos).when(this).getAppOpsService();
+            final AppOpsManager aos = mock(AppOpsManager.class);
+            doReturn(aos).when(this).getAppOpsManager();
             // Make sure permission checks aren't overridden.
-            doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOperation(anyInt(), anyInt(),
-                    anyString(), nullable(String.class), anyBoolean(), nullable(String.class));
+            doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOpNoThrow(anyInt(), anyInt(),
+                    anyString(), nullable(String.class), nullable(String.class));
 
             // UserManagerService
             final UserManagerService ums = mock(UserManagerService.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 7aaf3fb..52b465f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -20,7 +20,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
+import static com.android.internal.policy.TaskResizingAlgorithm.MIN_ASPECT;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index ebf14d2..feadd0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -988,7 +988,7 @@
     private Task createTask(int taskId) {
         return new ActivityStack(mService, taskId, new Intent(), null, null, null,
                 ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
-                0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0,
+                0, false, null, 0, 0, 0, 0, 0, null, null, 0, false, false, false, 0,
                 0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/,
                 null /*stack*/);
     }
@@ -1022,7 +1022,7 @@
                 boolean neverRelinquishIdentity,
                 ActivityManager.TaskDescription lastTaskDescription,
                 int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-                int callingUid, String callingPackage, int resizeMode,
+                int callingUid, String callingPackage, String callingFeatureId, int resizeMode,
                 boolean supportsPictureInPicture,
                 boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
                 int minHeight, ActivityStack stack) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d5eec33..8378d8e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -747,7 +747,8 @@
         }
 
         @Override
-        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
+        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
+                String callingFeatureId) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -757,8 +758,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
-                            intent, resolvedType);
+                    return mImpl.startVoiceActivityLocked(callingFeatureId, callingPid, callingUid,
+                            token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -766,7 +767,8 @@
         }
 
         @Override
-        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType) {
+        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
+                String callingFeatureId) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -776,8 +778,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startAssistantActivityLocked(callingPid, callingUid, token,
-                            intent, resolvedType);
+                    return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
+                            callingUid, token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index a1210cf..a62b03c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -216,8 +216,8 @@
         return true;
     }
 
-    public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token,
-            Intent intent, String resolvedType) {
+    public int startVoiceActivityLocked(@Nullable String callingFeatureId, int callingPid,
+            int callingUid, IBinder token, Intent intent, String resolvedType) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startVoiceActivity does not match active session");
@@ -230,16 +230,16 @@
             intent = new Intent(intent);
             intent.addCategory(Intent.CATEGORY_VOICE);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-            return mAtm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
-                    intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor,
-                    0, null, null, mUser);
+            return mAtm.startVoiceActivity(mComponent.getPackageName(), callingFeatureId,
+                    callingPid, callingUid, intent, resolvedType, mActiveSession.mSession,
+                    mActiveSession.mInteractor, 0, null, null, mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
     }
 
-    public int startAssistantActivityLocked(int callingPid, int callingUid, IBinder token,
-            Intent intent, String resolvedType) {
+    public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
+            int callingUid, IBinder token, Intent intent, String resolvedType) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -253,8 +253,8 @@
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             final ActivityOptions options = ActivityOptions.makeBasic();
             options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
-            return mAtm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid,
-                    intent, resolvedType, options.toBundle(), mUser);
+            return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
+                    callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index e16fffa..e1aec0a 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.service.euicc.EuiccProfileInfo;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -168,7 +169,12 @@
                     new IGetAllProfilesCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
-                            executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -192,7 +198,12 @@
                     new IGetProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            executor.execute(() -> callback.onComplete(resultCode, profile));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -217,7 +228,12 @@
                     refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -243,7 +259,12 @@
                     refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            executor.execute(() -> callback.onComplete(resultCode, profile));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -268,7 +289,12 @@
                     nickname, new ISetNicknameCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -292,7 +318,12 @@
                     new IDeleteProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -317,7 +348,12 @@
                     new IResetMemoryCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -340,7 +376,12 @@
                     new IGetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            executor.execute(() -> callback.onComplete(resultCode, address));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -363,7 +404,12 @@
                     new IGetSmdsAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            executor.execute(() -> callback.onComplete(resultCode, address));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -388,7 +434,12 @@
                     new ISetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -411,7 +462,12 @@
                     new IGetRulesAuthTableCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
-                            executor.execute(() -> callback.onComplete(resultCode, rat));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, rat));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -434,7 +490,12 @@
                     new IGetEuiccChallengeCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] challenge) {
-                            executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -457,7 +518,12 @@
                     new IGetEuiccInfo1Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            executor.execute(() -> callback.onComplete(resultCode, info));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -480,7 +546,12 @@
                     new IGetEuiccInfo2Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            executor.execute(() -> callback.onComplete(resultCode, info));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -522,7 +593,12 @@
                     new IAuthenticateServerCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -561,7 +637,12 @@
                     new IPrepareDownloadCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -589,7 +670,12 @@
                     new ILoadBoundProfilePackageCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -619,7 +705,12 @@
                     new ICancelSessionCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -643,7 +734,13 @@
                     new IListNotificationsCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            executor.execute(() -> callback.onComplete(resultCode, notifications));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -667,7 +764,13 @@
                     events, new IRetrieveNotificationListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            executor.execute(() -> callback.onComplete(resultCode, notifications));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -691,7 +794,13 @@
                     seqNumber, new IRetrieveNotificationCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification notification) {
-                            executor.execute(() -> callback.onComplete(resultCode, notification));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notification));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -718,7 +827,12 @@
                     new IRemoveNotificationFromListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 1361df3..5904916 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -321,7 +321,8 @@
                 }
 
                 mAtm.startActivityAndWait(null,
-                        getInstrumentation().getContext().getBasePackageName(), mLaunchIntent,
+                        getInstrumentation().getContext().getBasePackageName(),
+                        getInstrumentation().getContext().getFeatureId(), mLaunchIntent,
                         mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
                         UserHandle.USER_CURRENT_OR_SELF);
             } catch (RemoteException e) {
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 98e7b4e..89005da 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -36,6 +36,15 @@
 }
 
 java_test_host {
+    name: "NetworkStagedRollbackTest",
+    srcs: ["NetworkStagedRollbackTest/src/**/*.java"],
+    libs: ["tradefed"],
+    static_libs: ["testng"],
+    test_suites: ["general-tests"],
+    test_config: "NetworkStagedRollbackTest.xml",
+}
+
+java_test_host {
     name: "MultiUserRollbackTest",
     srcs: ["MultiUserRollbackTest/src/**/*.java"],
     libs: ["tradefed"],
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest.xml b/tests/RollbackTest/NetworkStagedRollbackTest.xml
new file mode 100644
index 0000000..a465a4f
--- /dev/null
+++ b/tests/RollbackTest/NetworkStagedRollbackTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs the network staged rollback tests">
+    <option name="test-suite-tag" value="NetworkStagedRollbackTest" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="RollbackTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="class" value="com.android.tests.rollback.host.NetworkStagedRollbackTest" />
+    </test>
+</configuration>
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
new file mode 100644
index 0000000..2c2e828
--- /dev/null
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.host;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs the network rollback tests.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class NetworkStagedRollbackTest extends BaseHostJUnit4Test {
+    /**
+     * Tests failed network health check triggers watchdog staged rollbacks.
+     */
+    @Test
+    public void testNetworkFailedRollback() throws Exception {
+    }
+
+    /**
+     * Tests passed network health check does not trigger watchdog staged rollbacks.
+     */
+    @Test
+    public void testNetworkPassedDoesNotRollback() throws Exception {
+    }
+}
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index a14b01c..f2c0f86 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -22,9 +22,9 @@
         <option name="package" value="com.android.tests.rollback" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
 
-        <!-- Exclude the StagedRollbackTest and MultiUserRollbackTest tests, which need to be
-             specially driven from the StagedRollbackTest and MultiUserRollbackTest host test -->
+        <!-- Exclude the device tests which need to be specially driven from the host tests -->
         <option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" />
+        <option name="exclude-filter" value="com.android.tests.rollback.NetworkStagedRollbackTest" />
         <option name="exclude-filter" value="com.android.tests.rollback.MultiUserRollbackTest" />
     </test>
 </configuration>
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
similarity index 64%
copy from core/java/android/os/StatsDimensionsValue.aidl
copy to tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
index 81a14a4..04004d6 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-package android.os;
+package com.android.tests.rollback;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NetworkStagedRollbackTest {
+}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index c8fd243..0db3313 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -519,6 +519,9 @@
             case BAND_5GHZ:
                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_5GHZ;
                 break;
+            case BAND_2GHZ | BAND_5GHZ:
+                wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
+                break;
             case BAND_ANY:
                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
                 break;
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 2efdd97..d958488 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -287,17 +287,43 @@
 
     @Test
     public void testToWifiConfigurationWithSupportedParameter() {
-        SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
+        SoftApConfiguration softApConfig_2g = new SoftApConfiguration.Builder()
+                .setPassphrase("secretsecret",
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+                .setChannel(11, SoftApConfiguration.BAND_2GHZ)
+                .setHiddenSsid(true)
+                .build();
+        WifiConfiguration wifiConfig_2g = softApConfig_2g.toWifiConfiguration();
+        assertThat(wifiConfig_2g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        assertThat(wifiConfig_2g.preSharedKey).isEqualTo("secretsecret");
+        assertThat(wifiConfig_2g.apBand).isEqualTo(WifiConfiguration.AP_BAND_2GHZ);
+        assertThat(wifiConfig_2g.apChannel).isEqualTo(11);
+        assertThat(wifiConfig_2g.hiddenSSID).isEqualTo(true);
+
+        SoftApConfiguration softApConfig_5g = new SoftApConfiguration.Builder()
                 .setPassphrase("secretsecret",
                         SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setChannel(149, SoftApConfiguration.BAND_5GHZ)
                 .setHiddenSsid(true)
                 .build();
-        WifiConfiguration wifiConfig = softApConfig.toWifiConfiguration();
-        assertThat(wifiConfig.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
-        assertThat(wifiConfig.preSharedKey).isEqualTo("secretsecret");
-        assertThat(wifiConfig.apBand).isEqualTo(WifiConfiguration.AP_BAND_5GHZ);
-        assertThat(wifiConfig.apChannel).isEqualTo(149);
-        assertThat(wifiConfig.hiddenSSID).isEqualTo(true);
+        WifiConfiguration wifiConfig_5g = softApConfig_5g.toWifiConfiguration();
+        assertThat(wifiConfig_5g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        assertThat(wifiConfig_5g.preSharedKey).isEqualTo("secretsecret");
+        assertThat(wifiConfig_5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_5GHZ);
+        assertThat(wifiConfig_5g.apChannel).isEqualTo(149);
+        assertThat(wifiConfig_5g.hiddenSSID).isEqualTo(true);
+
+        SoftApConfiguration softApConfig_2g5g = new SoftApConfiguration.Builder()
+                .setPassphrase("secretsecret",
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+                .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+        WifiConfiguration wifiConfig_2g5g = softApConfig_2g5g.toWifiConfiguration();
+        assertThat(wifiConfig_2g5g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        assertThat(wifiConfig_2g5g.preSharedKey).isEqualTo("secretsecret");
+        assertThat(wifiConfig_2g5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_ANY);
+        assertThat(wifiConfig_2g5g.apChannel).isEqualTo(0);
+        assertThat(wifiConfig_2g5g.hiddenSSID).isEqualTo(true);
     }
 }