Merge "Allow system listeners to unsnooze"
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 08b1c2b..abf78c6 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -56,6 +56,12 @@
  * instantiate this class directly; instead, retrieve it through
  * {@link android.content.Context#getSystemService
  * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.
+ *
+ * <p class="caution"><strong>Note:</strong> Beginning with API 30
+ * ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications.
+ * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency is indicative
+ * of an app bug and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin
+ * to throttle apps that show buggy behavior, regardless of target SDK version.
  */
 @SystemService(Context.JOB_SCHEDULER_SERVICE)
 public abstract class JobScheduler {
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 4ffcf8a..4b4fb96 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -80,27 +80,22 @@
     }
 
     /**
-     * Add the specified package to the power save whitelist.
-     *
-     * @return true if the package was successfully added to the whitelist
+     * Add the specified package to the permanent power save whitelist.
      */
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
-    public boolean addToWhitelist(@NonNull String packageName) {
-        return addToWhitelist(Collections.singletonList(packageName)) == 1;
+    public void addToWhitelist(@NonNull String packageName) {
+        addToWhitelist(Collections.singletonList(packageName));
     }
 
     /**
-     * Add the specified packages to the power save whitelist.
-     *
-     * @return the number of packages that were successfully added to the whitelist
+     * Add the specified packages to the permanent power save whitelist.
      */
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
-    public int addToWhitelist(@NonNull List<String> packageNames) {
+    public void addToWhitelist(@NonNull List<String> packageNames) {
         try {
-            return mService.addPowerSaveWhitelistApps(packageNames);
+            mService.addPowerSaveWhitelistApps(packageNames);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
-            return 0;
         }
     }
 
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index d2d942a..dc72d6d 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -85,6 +85,7 @@
     /**
      * Checks if an app has been idle for a while and filters out apps that are excluded.
      * It returns false if the current system state allows all apps to be considered active.
+     * This happens if the device is plugged in or otherwise temporarily allowed to make exceptions.
      * Called by interface impls.
      */
     boolean isAppIdleFiltered(String packageName, int appId, int userId,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index b516279..e4c6b52 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -37,8 +37,6 @@
 import android.app.job.JobWorkItem;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -57,7 +55,6 @@
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -90,7 +87,6 @@
 import com.android.server.DeviceIdleInternal;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.compat.PlatformCompat;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
 import com.android.server.job.controllers.BackgroundJobsController;
@@ -155,16 +151,6 @@
     /** The maximum number of jobs that we allow an unprivileged app to schedule */
     private static final int MAX_JOBS_PER_APP = 100;
 
-    /**
-     * {@link #schedule(JobInfo)}, {@link #scheduleAsPackage(JobInfo, String, int, String)}, and
-     * {@link #enqueue(JobInfo, JobWorkItem)} will throw a {@link IllegalStateException} if the app
-     * calls the APIs too frequently.
-     */
-    @ChangeId
-    // This means the change will be enabled for target SDK larger than 29 (Q), meaning R and up.
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
-    protected static final long CRASH_ON_EXCEEDED_LIMIT = 144363383L;
-
     @VisibleForTesting
     public static Clock sSystemClock = Clock.systemUTC();
 
@@ -264,7 +250,6 @@
 
     private final CountQuotaTracker mQuotaTracker;
     private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
-    private final PlatformCompat mPlatformCompat;
 
     /**
      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -986,9 +971,7 @@
                 Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
                 mAppStandbyInternal.restrictApp(
                         pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
-                if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION
-                        && mPlatformCompat.isChangeEnabledByPackageName(
-                                CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) {
+                if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
                     final boolean isDebuggable;
                     synchronized (mLock) {
                         if (!mDebuggableApps.containsKey(packageName)) {
@@ -1370,8 +1353,6 @@
         // Set up the app standby bucketing tracker
         mStandbyTracker = new StandbyTracker();
         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
-        mPlatformCompat =
-                (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
         mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER);
         mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
                 mConstants.API_QUOTA_SCHEDULE_COUNT,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index f1bfa04..e343478 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -48,6 +48,7 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
 import android.annotation.NonNull;
@@ -71,9 +72,8 @@
 import android.database.ContentObserver;
 import android.hardware.display.DisplayManager;
 import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkRequest;
 import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.Environment;
@@ -285,6 +285,7 @@
     long mInitialForegroundServiceStartTimeoutMillis;
 
     private volatile boolean mAppIdleEnabled;
+    private boolean mIsCharging;
     private boolean mSystemServicesReady = false;
     // There was a system update, defaults need to be initialized after services are ready
     private boolean mPendingInitializeDefaults;
@@ -360,6 +361,11 @@
         mHandler = new AppStandbyHandler(mInjector.getLooper());
         mPackageManager = mContext.getPackageManager();
 
+        DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
+        IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
+        deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+        mContext.registerReceiver(deviceStateReceiver, deviceStates);
+
         synchronized (mAppIdleLock) {
             mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
                     mInjector.elapsedRealtime());
@@ -417,6 +423,8 @@
             if (mPendingOneTimeCheckIdleStates) {
                 postOneTimeCheckIdleStates();
             }
+        } else if (phase == PHASE_BOOT_COMPLETED) {
+            setChargingState(mInjector.isCharging());
         }
     }
 
@@ -515,6 +523,16 @@
                 appUsage.bucketingReason, false);
     }
 
+    @VisibleForTesting
+    void setChargingState(boolean isCharging) {
+        synchronized (mAppIdleLock) {
+            if (mIsCharging != isCharging) {
+                if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
+                mIsCharging = isCharging;
+            }
+        }
+    }
+
     @Override
     public void postCheckIdleStates(int userId) {
         mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
@@ -977,6 +995,11 @@
         if (isAppSpecial(packageName, appId, userId)) {
             return false;
         } else {
+            synchronized (mAppIdleLock) {
+                if (!mAppIdleEnabled || mIsCharging) {
+                    return false;
+                }
+            }
             return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
         }
     }
@@ -1543,6 +1566,8 @@
 
         pw.println();
         pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+        pw.print(" mIsCharging=");
+        pw.print(mIsCharging);
         pw.println();
         pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
         pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
@@ -1560,6 +1585,7 @@
         private final Looper mLooper;
         private IDeviceIdleController mDeviceIdleController;
         private IBatteryStats mBatteryStats;
+        private BatteryManager mBatteryManager;
         private PackageManagerInternal mPackageManagerInternal;
         private DisplayManager mDisplayManager;
         private PowerManager mPowerManager;
@@ -1593,6 +1619,7 @@
                 mDisplayManager = (DisplayManager) mContext.getSystemService(
                         Context.DISPLAY_SERVICE);
                 mPowerManager = mContext.getSystemService(PowerManager.class);
+                mBatteryManager = mContext.getSystemService(BatteryManager.class);
 
                 final ActivityManager activityManager =
                         (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -1630,6 +1657,10 @@
             return buildFlag && runtimeFlag;
         }
 
+        boolean isCharging() {
+            return mBatteryManager.isCharging();
+        }
+
         boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
             return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
         }
@@ -1766,15 +1797,19 @@
         }
     };
 
-    private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build();
-
-    private final ConnectivityManager.NetworkCallback mNetworkCallback
-            = new ConnectivityManager.NetworkCallback() {
+    private class DeviceStateReceiver extends BroadcastReceiver {
         @Override
-        public void onAvailable(Network network) {
-            mConnectivityManager.unregisterNetworkCallback(this);
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case BatteryManager.ACTION_CHARGING:
+                    setChargingState(true);
+                    break;
+                case BatteryManager.ACTION_DISCHARGING:
+                    setChargingState(false);
+                    break;
+            }
         }
-    };
+    }
 
     private final DisplayManager.DisplayListener mDisplayListener
             = new DisplayManager.DisplayListener() {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 3eed26b..7d18578 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -50,6 +50,7 @@
 import com.google.android.exoplayer2.upstream.DataSpec;
 import com.google.android.exoplayer2.upstream.TransferListener;
 import com.google.android.exoplayer2.util.ParsableByteArray;
+import com.google.android.exoplayer2.video.ColorInfo;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -810,19 +811,17 @@
     // Private static methods.
 
     private static MediaFormat toMediaFormat(Format format) {
-
         MediaFormat result = new MediaFormat();
-        if (format.bitrate != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
-        }
-        if (format.channelCount != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
-        }
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
 
-        if (format.colorInfo != null) {
-            result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer);
-            result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange);
-            result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace);
+        ColorInfo colorInfo = format.colorInfo;
+        if (colorInfo != null) {
+            setOptionalMediaFormatInt(
+                    result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
+            setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange);
+            setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace);
+
             if (format.colorInfo.hdrStaticInfo != null) {
                 result.setByteBuffer(
                         MediaFormat.KEY_HDR_STATIC_INFO,
@@ -830,63 +829,50 @@
             }
         }
 
-        if (format.sampleMimeType != null) {
-            result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
-        }
-        if (format.codecs != null) {
-            result.setString(MediaFormat.KEY_CODECS_STRING, format.codecs);
-        }
+        setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType);
+        setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs);
         if (format.frameRate != Format.NO_VALUE) {
             result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
         }
-        if (format.width != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_WIDTH, format.width);
-        }
-        if (format.height != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
-        }
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height);
+
         List<byte[]> initData = format.initializationData;
         if (initData != null) {
             for (int i = 0; i < initData.size(); i++) {
                 result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
             }
         }
-        if (format.language != null) {
-            result.setString(MediaFormat.KEY_LANGUAGE, format.language);
-        }
-        if (format.maxInputSize != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
-        }
-        if (format.pcmEncoding != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
-        }
-        if (format.rotationDegrees != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
-        }
-        if (format.sampleRate != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
-        }
+        setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
+
         int selectionFlags = format.selectionFlags;
-        if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) {
-            result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1);
+        result.setInteger(
+                MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT);
+        result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT);
+        result.setInteger(
+                MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED);
+
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
+        setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
+
+        if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) {
+            int parWidth = 1;
+            int parHeight = 1;
+            if (format.pixelWidthHeightRatio < 1.0f) {
+                parHeight = 1 << 30;
+                parWidth = (int) (format.pixelWidthHeightRatio * parHeight);
+            } else if (format.pixelWidthHeightRatio > 1.0f) {
+                parWidth = 1 << 30;
+                parHeight = (int) (parWidth / format.pixelWidthHeightRatio);
+            }
+            result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth);
+            result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight);
+            result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio);
         }
-        if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) {
-            result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1);
-        }
-        if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) {
-            result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1);
-        }
-        if (format.encoderDelay != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
-        }
-        if (format.encoderPadding != Format.NO_VALUE) {
-            result.setInteger(MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
-        }
-        // TODO: Implement float to fraction conversion.
-        // if (format.pixelWidthHeightRatio != Format.NO_VALUE) {
-        //     result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, );
-        //     result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, );
-        // }
 
         // LACK OF SUPPORT FOR:
         //    format.accessibilityChannel;
@@ -899,6 +885,19 @@
         return result;
     }
 
+    private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) {
+        if (value != Format.NO_VALUE) {
+            mediaFormat.setInteger(key, value);
+        }
+    }
+
+    private static void setOptionalMediaFormatString(
+            MediaFormat mediaFormat, String key, @Nullable String value) {
+        if (value != null) {
+            mediaFormat.setString(key, value);
+        }
+    }
+
     private static DrmInitData toFrameworkDrmInitData(
             com.google.android.exoplayer2.drm.DrmInitData drmInitData) {
         // TODO: Implement.
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 1f9f18c..c0f84a0 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -20,6 +20,7 @@
 
 apex_defaults {
     native_shared_libs: [
+        "libstatspull",
         "libstats_jni",
     ],
     // binaries: ["vold"],
@@ -28,6 +29,7 @@
         "service-statsd",
     ],
     // prebuilts: ["my_prebuilt"],
+    compile_multilib: "both",
     name: "com.android.os.statsd-defaults",
     key: "com.android.os.statsd.key",
     certificate: ":com.android.os.statsd.certificate",
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 7c93bc7..4ccdd7e 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -38,6 +38,10 @@
         },
         ndk: {
             enabled: true,
+            apex_available: [
+                "com.android.os.statsd",
+            ],
         }
-    }
+
+    },
 }
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 63a853a..ab669d4 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_visibility: [ ":__pkg__" ]
+}
+
 genrule {
     name: "statslog-statsd-java-gen",
     tools: ["stats-log-api-gen"],
@@ -25,6 +29,9 @@
     srcs: [
         ":statslog-statsd-java-gen",
     ],
+    visibility: [
+        "//cts/hostsidetests/statsd/apps:__subpackages__",
+    ]
 }
 
 filegroup {
@@ -34,6 +41,9 @@
         ":framework-statsd-aidl-sources",
         ":statslog-statsd-java-gen",
     ],
+    visibility: [
+        "//frameworks/base", // For the "global" stubs.
+    ],
 }
 
 java_defaults {
@@ -139,6 +149,10 @@
         "framework-statsd-defaults",
     ],
     srcs: [ ":framework-statsd-stubs-srcs-publicapi" ],
+    visibility: [
+        "//frameworks/base", // Framework
+        "//frameworks/base/apex/statsd", // statsd apex
+    ]
 }
 
 java_library {
@@ -147,6 +161,10 @@
         "framework-statsd-defaults",
     ],
     srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
+    visibility: [
+        "//frameworks/base", // Framework
+        "//frameworks/base/apex/statsd", // statsd apex
+    ]
 }
 
 java_library {
@@ -155,4 +173,9 @@
         "framework-statsd-defaults",
     ],
     srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ],
+    visibility: [
+        "//frameworks/base", // Framework
+        "//frameworks/base/apex/statsd", // statsd apex
+        "//frameworks/opt/net/wifi/service" // wifi service
+    ]
 }
diff --git a/api/current.txt b/api/current.txt
index ac52e58..0290586 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -289,6 +289,7 @@
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
+    field public static final int allowNativeHeapPointerTagging = 16844311; // 0x1010617
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -2876,6 +2877,7 @@
     method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
     method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>);
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
+    field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
     field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
     field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
     field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
@@ -2883,6 +2885,7 @@
     field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
     field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
+    field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
     field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
     field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
     field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
@@ -2890,6 +2893,7 @@
     field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
     field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
     field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
+    field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
     field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
     field public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; // 0x22
     field public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; // 0x23
@@ -10141,7 +10145,6 @@
     field public static final String ALARM_SERVICE = "alarm";
     field public static final String APPWIDGET_SERVICE = "appwidget";
     field public static final String APP_OPS_SERVICE = "appops";
-    field public static final String APP_SEARCH_SERVICE = "app_search";
     field public static final String AUDIO_SERVICE = "audio";
     field public static final String BATTERY_SERVICE = "batterymanager";
     field public static final int BIND_ABOVE_CLIENT = 8; // 0x8
@@ -12703,8 +12706,7 @@
 
   public class Resources {
     ctor @Deprecated public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
-    method public void addLoader(@NonNull android.content.res.loader.ResourcesLoader);
-    method public void clearLoaders();
+    method public void addLoaders(@NonNull android.content.res.loader.ResourcesLoader...);
     method public final void finishPreloading();
     method public final void flushLayoutCache();
     method @NonNull public android.content.res.XmlResourceParser getAnimation(@AnimRes @AnimatorRes int) throws android.content.res.Resources.NotFoundException;
@@ -12731,7 +12733,6 @@
     method @NonNull public int[] getIntArray(@ArrayRes int) throws android.content.res.Resources.NotFoundException;
     method public int getInteger(@IntegerRes int) throws android.content.res.Resources.NotFoundException;
     method @NonNull public android.content.res.XmlResourceParser getLayout(@LayoutRes int) throws android.content.res.Resources.NotFoundException;
-    method @NonNull public java.util.List<android.content.res.loader.ResourcesLoader> getLoaders();
     method @Deprecated public android.graphics.Movie getMovie(@RawRes int) throws android.content.res.Resources.NotFoundException;
     method @NonNull public String getQuantityString(@PluralsRes int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException;
     method @NonNull public String getQuantityString(@PluralsRes int, int) throws android.content.res.Resources.NotFoundException;
@@ -12759,8 +12760,7 @@
     method public android.content.res.AssetFileDescriptor openRawResourceFd(@RawRes int) throws android.content.res.Resources.NotFoundException;
     method public void parseBundleExtra(String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
     method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void removeLoader(@NonNull android.content.res.loader.ResourcesLoader);
-    method public void setLoaders(@NonNull java.util.List<android.content.res.loader.ResourcesLoader>);
+    method public void removeLoaders(@NonNull android.content.res.loader.ResourcesLoader...);
     method @Deprecated public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
     field @AnyRes public static final int ID_NULL = 0; // 0x0
   }
@@ -45514,7 +45514,7 @@
     field public static final int DIRECTION_INCOMING = 0; // 0x0
     field public static final int DIRECTION_OUTGOING = 1; // 0x1
     field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff
-    field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
+    field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
     field public static final int PROPERTY_CONFERENCE = 1; // 0x1
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -45603,7 +45603,8 @@
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
     method public final int getConnectionCapabilities();
     method public final int getConnectionProperties();
-    method public final long getConnectionTime();
+    method public final long getConnectionStartElapsedRealtimeMillis();
+    method @IntRange(from=0) public final long getConnectionTime();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
@@ -45633,8 +45634,9 @@
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConnectionCapabilities(int);
     method public final void setConnectionProperties(int);
-    method public final void setConnectionStartElapsedRealTime(long);
-    method public final void setConnectionTime(long);
+    method @Deprecated public final void setConnectionStartElapsedRealTime(long);
+    method public final void setConnectionStartElapsedRealtimeMillis(long);
+    method public final void setConnectionTime(@IntRange(from=0) long);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setExtras(@Nullable android.os.Bundle);
@@ -45794,7 +45796,7 @@
     field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
     field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
-    field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
+    field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
@@ -47133,6 +47135,19 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR;
   }
 
+  public final class DisplayInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getNetworkType();
+    method public int getOverrideNetworkType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DisplayInfo> CREATOR;
+    field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
+    field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1
+    field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0
+    field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3
+    field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
+  }
+
   public class IccOpenLogicalChannelResponse implements android.os.Parcelable {
     method public int describeContents();
     method public int getChannel();
@@ -47389,6 +47404,7 @@
     method public void onDataActivity(int);
     method public void onDataConnectionStateChanged(int);
     method public void onDataConnectionStateChanged(int, int);
+    method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.DisplayInfo);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void onMessageWaitingIndicatorChanged(boolean);
     method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
@@ -47406,6 +47422,7 @@
     field public static final int LISTEN_CELL_LOCATION = 16; // 0x10
     field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
     field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
+    field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
     field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
     field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index b07066d..8725538 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1809,7 +1809,7 @@
     field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
     field public static final String TETHERING_SERVICE = "tethering";
     field public static final String VR_SERVICE = "vrmanager";
-    field public static final String WIFI_COND_SERVICE = "wificond";
+    field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
     field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
   }
@@ -2217,6 +2217,7 @@
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
+    field public static final int FLAG_PERMISSION_AUTO_REVOKED = 1048576; // 0x100000
     field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000
     field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000
     field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
@@ -2302,7 +2303,7 @@
     method public void onPermissionsChanged(int);
   }
 
-  @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+  @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
   }
 
   public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -8329,21 +8330,21 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.RadioChainInfo> CREATOR;
   }
 
-  public class WifiCondManager {
+  public class WifiNl80211Manager {
     method public void abortScan(@NonNull String);
     method public void enableVerboseLogging(boolean);
     method @NonNull public int[] getChannelsMhzForBand(int);
     method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
     method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
-    method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
-    method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
-    method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback);
-    method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback);
+    method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
+    method @Nullable public static android.net.wifi.wificond.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
+    method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback);
+    method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SendMgmtFrameCallback);
     method public void setOnServiceDeadCallback(@NonNull Runnable);
-    method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback);
+    method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback);
     method public boolean setupInterfaceForSoftApMode(@NonNull String);
-    method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String);
-    method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.PnoScanRequestCallback);
+    method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String);
+    method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.PnoScanRequestCallback);
     method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
     method public boolean stopPnoScan(@NonNull String);
     method public boolean tearDownClientInterface(@NonNull String);
@@ -8358,43 +8359,43 @@
     field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
   }
 
-  public static class WifiCondManager.OemSecurityType {
-    ctor public WifiCondManager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
+  public static class WifiNl80211Manager.OemSecurityType {
+    ctor public WifiNl80211Manager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
     field public final int groupCipher;
     field @NonNull public final java.util.List<java.lang.Integer> keyManagement;
     field @NonNull public final java.util.List<java.lang.Integer> pairwiseCipher;
     field public final int protocol;
   }
 
-  public static interface WifiCondManager.PnoScanRequestCallback {
+  public static interface WifiNl80211Manager.PnoScanRequestCallback {
     method public void onPnoRequestFailed();
     method public void onPnoRequestSucceeded();
   }
 
-  public static interface WifiCondManager.ScanEventCallback {
+  public static interface WifiNl80211Manager.ScanEventCallback {
     method public void onScanFailed();
     method public void onScanResultReady();
   }
 
-  public static interface WifiCondManager.SendMgmtFrameCallback {
+  public static interface WifiNl80211Manager.SendMgmtFrameCallback {
     method public void onAck(int);
     method public void onFailure(int);
   }
 
-  public static class WifiCondManager.SignalPollResult {
+  public static class WifiNl80211Manager.SignalPollResult {
     field public final int associationFrequencyMHz;
     field public final int currentRssiDbm;
     field public final int rxBitrateMbps;
     field public final int txBitrateMbps;
   }
 
-  public static interface WifiCondManager.SoftApCallback {
+  public static interface WifiNl80211Manager.SoftApCallback {
     method public void onConnectedClientsChanged(@NonNull android.net.wifi.wificond.NativeWifiClient, boolean);
     method public void onFailure();
     method public void onSoftApChannelSwitched(int, int);
   }
 
-  public static class WifiCondManager.TxPacketCounters {
+  public static class WifiNl80211Manager.TxPacketCounters {
     field public final int txPacketFailed;
     field public final int txPacketSucceeded;
   }
@@ -8839,8 +8840,8 @@
   }
 
   public class PowerWhitelistManager {
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean addToWhitelist(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
     field public static final int EVENT_MMS = 2; // 0x2
@@ -10854,27 +10855,26 @@
   public abstract class Conference extends android.telecom.Conferenceable {
     method @Deprecated public final android.telecom.AudioState getAudioState();
     method @Deprecated public final long getConnectTimeMillis();
-    method public final long getConnectionStartElapsedRealTime();
     method public android.telecom.Connection getPrimaryConnection();
     method @NonNull public final String getTelecomCallId();
     method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
-    method public final void setAddress(@NonNull android.net.Uri, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int);
     method public final void setCallerDisplayName(@NonNull String, int);
-    method public void setConferenceState(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean);
     method @Deprecated public final void setConnectTimeMillis(long);
   }
 
   public abstract class Connection extends android.telecom.Conferenceable {
     method @Deprecated public final android.telecom.AudioState getAudioState();
-    method public final long getConnectElapsedTimeMillis();
-    method public final long getConnectTimeMillis();
+    method @IntRange(from=0) public final long getConnectTimeMillis();
+    method public final long getConnectionStartElapsedRealtimeMillis();
     method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method @Nullable public final String getTelecomCallId();
     method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
     method public final void resetConnectionTime();
     method public void setCallDirection(int);
-    method public final void setConnectTimeMillis(long);
-    method public final void setConnectionStartElapsedRealTime(long);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long);
     method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
     method public void setTelecomCallId(@NonNull String);
     field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
@@ -11033,7 +11033,7 @@
   }
 
   public static class PhoneAccount.Builder {
-    method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
   }
 
   public class PhoneAccountSuggestionService extends android.app.Service {
@@ -11108,7 +11108,7 @@
     method public int getCallState();
     method public android.telecom.PhoneAccountHandle getConnectionManager();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
     method @Deprecated public android.content.ComponentName getDefaultPhoneApp();
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
@@ -12572,6 +12572,7 @@
     method public void notifyDataActivityChanged(int, int);
     method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
     method public void notifyDisconnectCause(int, int, int, int);
+    method public void notifyDisplayInfoChanged(int, int, @NonNull android.telephony.DisplayInfo);
     method public void notifyEmergencyNumberList(int, int);
     method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
     method public void notifyMessageWaitingChanged(int, int, boolean);
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 0caee6b..dfb0d74 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -234,7 +234,7 @@
     If implemented by developer, should follow the on<Something> style; otherwise consider marking final
 
 
-PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
+PairedRegistration: android.net.wifi.wificond.WifiNl80211Manager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback):
     
 
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 84fd6f4..4c8bb02 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2451,8 +2451,8 @@
   }
 
   public class PowerWhitelistManager {
-    method @RequiresPermission("android.permission.DEVICE_POWER") public boolean addToWhitelist(@NonNull String);
-    method @RequiresPermission("android.permission.DEVICE_POWER") public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
+    method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull String);
+    method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
     method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
     method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
     field public static final int EVENT_MMS = 2; // 0x2
@@ -3441,23 +3441,22 @@
   }
 
   public abstract class Conference extends android.telecom.Conferenceable {
-    method public final long getConnectionStartElapsedRealTime();
     method public android.telecom.Connection getPrimaryConnection();
     method @NonNull public final String getTelecomCallId();
-    method public final void setAddress(@NonNull android.net.Uri, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int);
     method public final void setCallerDisplayName(@NonNull String, int);
-    method public void setConferenceState(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean);
   }
 
   public abstract class Connection extends android.telecom.Conferenceable {
-    method public final long getConnectElapsedTimeMillis();
-    method public final long getConnectTimeMillis();
+    method @IntRange(from=0) public final long getConnectTimeMillis();
+    method public final long getConnectionStartElapsedRealtimeMillis();
     method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method @Nullable public final String getTelecomCallId();
     method public final void resetConnectionTime();
     method public void setCallDirection(int);
-    method public final void setConnectTimeMillis(long);
-    method public final void setConnectionStartElapsedRealTime(long);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long);
     method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
     method public void setTelecomCallId(@NonNull String);
     field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
@@ -3489,7 +3488,7 @@
   }
 
   public static class PhoneAccount.Builder {
-    method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
   }
 
   public class PhoneAccountSuggestionService extends android.app.Service {
@@ -3503,7 +3502,7 @@
   public class TelecomManager {
     method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode();
-    method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(int);
+    method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
     field public static final int TTY_MODE_FULL = 1; // 0x1
@@ -3721,6 +3720,7 @@
     method public void notifyDataActivityChanged(int, int);
     method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
     method public void notifyDisconnectCause(int, int, int, int);
+    method public void notifyDisplayInfoChanged(int, int, @NonNull android.telephony.DisplayInfo);
     method public void notifyEmergencyNumberList(int, int);
     method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
     method public void notifyMessageWaitingChanged(int, int, boolean);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 03f97d8..23a4437 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -381,7 +381,7 @@
         UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"];
         NotificationReported notification_reported = 244 [(module) = "framework"];
         NotificationPanelReported notification_panel_reported = 245;
-        NotificationChannelModified notification_panel_modified = 246;
+        NotificationChannelModified notification_channel_modified = 246;
         IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
         IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"];
         CellBroadcastMessageReported cb_message_reported =
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index ace13513..25729ab 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -18,6 +18,7 @@
 
 
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
@@ -25,6 +26,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
@@ -32,6 +34,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
@@ -83,9 +86,11 @@
     @IntDef(prefix = { "GESTURE_" }, value = {
             GESTURE_2_FINGER_SINGLE_TAP,
             GESTURE_2_FINGER_DOUBLE_TAP,
+            GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
             GESTURE_2_FINGER_TRIPLE_TAP,
             GESTURE_3_FINGER_SINGLE_TAP,
             GESTURE_3_FINGER_DOUBLE_TAP,
+            GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD,
             GESTURE_3_FINGER_TRIPLE_TAP,
             GESTURE_DOUBLE_TAP,
             GESTURE_DOUBLE_TAP_AND_HOLD,
@@ -114,6 +119,7 @@
             GESTURE_3_FINGER_SWIPE_RIGHT,
             GESTURE_3_FINGER_SWIPE_UP,
             GESTURE_4_FINGER_DOUBLE_TAP,
+            GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD,
             GESTURE_4_FINGER_SINGLE_TAP,
             GESTURE_4_FINGER_SWIPE_DOWN,
             GESTURE_4_FINGER_SWIPE_LEFT,
@@ -175,12 +181,18 @@
         switch (eventType) {
             case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
             case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
+            case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
+                return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
             case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP";
             case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP";
             case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP";
+            case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD:
+                return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD";
             case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP";
             case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP";
             case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP";
+            case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD:
+                return "GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD";
             case GESTURE_4_FINGER_TRIPLE_TAP: return "GESTURE_4_FINGER_TRIPLE_TAP";
             case GESTURE_DOUBLE_TAP: return "GESTURE_DOUBLE_TAP";
             case GESTURE_DOUBLE_TAP_AND_HOLD: return "GESTURE_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b65f68e..0ed6b1f 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -411,6 +411,15 @@
     /** The user has performed a four-finger triple tap gesture on the touch screen. */
     public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39;
 
+    /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
+    public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40;
+
+    /** The user has performed a three-finger double tap and hold gesture on the touch screen. */
+    public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
+
+    /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
+    public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 367c2f2..1de68ba 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7824,9 +7824,7 @@
      * @hide
      */
     public static boolean isCollectingNotedAppOps() {
-        synchronized (sLock) {
-            return sNotedAppOpsCollector != null;
-        }
+        return sNotedAppOpsCollector != null;
     }
 
     /**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 8b07418..9b73060 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -132,7 +132,7 @@
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
 import android.net.wifi.WifiFrameworkInitializer;
-import android.net.wifi.wificond.WifiCondManager;
+import android.net.wifi.wificond.WifiNl80211Manager;
 import android.nfc.NfcManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -761,11 +761,11 @@
                 return new EthernetManager(ctx.getOuterContext(), service);
             }});
 
-        registerService(Context.WIFI_COND_SERVICE, WifiCondManager.class,
-                new CachedServiceFetcher<WifiCondManager>() {
+        registerService(Context.WIFI_NL80211_SERVICE, WifiNl80211Manager.class,
+                new CachedServiceFetcher<WifiNl80211Manager>() {
                     @Override
-                    public WifiCondManager createService(ContextImpl ctx) {
-                        return new WifiCondManager(ctx.getOuterContext());
+                    public WifiNl80211Manager createService(ContextImpl ctx) {
+                        return new WifiNl80211Manager(ctx.getOuterContext());
                     }
                 });
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index dc11013..4a5a23a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4718,15 +4718,40 @@
     public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
 
     /**
-     * Keyguard features that when set on a managed profile that doesn't have its own challenge will
-     * affect the profile's parent user. These can also be set on the managed profile's parent
+     * Keyguard features that when set on a non-organization-owned managed profile that doesn't
+     * have its own challenge will affect the profile's parent user. These can also be set on the
+     * managed profile's parent {@link DevicePolicyManager} instance to explicitly control the
+     * parent user.
+     *
+     * <p>
+     * Organization-owned managed profile supports disabling additional keyguard features on the
+     * parent user as defined in {@link #ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY}.
+     *
+     * @hide
+     */
+    public static final int NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
+            DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
+            | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS;
+
+    /**
+     * Keyguard features that when set by the profile owner of an organization-owned managed
+     * profile will affect the profile's parent user if set on the managed profile's parent
      * {@link DevicePolicyManager} instance.
      *
      * @hide
      */
+    public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY =
+            KEYGUARD_DISABLE_SECURE_CAMERA;
+
+    /**
+     * Keyguard features that when set on a normal or organization-owned managed profile, have
+     * the potential to affect the profile's parent user.
+     *
+     * @hide
+     */
     public static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
-            DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
-            | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS;
+            DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER
+                    | DevicePolicyManager.ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY;
 
     /**
      * @deprecated This method does not actually modify the storage encryption of the device.
@@ -6115,11 +6140,20 @@
      * <li>{@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS} which affects notifications generated
      * by applications in the managed profile.
      * </ul>
+     * <p>
+     * From version {@link android.os.Build.VERSION_CODES#R} the profile owner of an
+     * organization-owned managed profile can set:
+     * <ul>
+     * <li>{@link #KEYGUARD_DISABLE_SECURE_CAMERA} which affects the parent user when called on the
+     * parent profile.
+     * </ul>
      * {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT},
-     * {@link #KEYGUARD_DISABLE_FACE} and {@link #KEYGUARD_DISABLE_IRIS} can also be
-     * set on the {@link DevicePolicyManager} instance returned by
-     * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
-     * profile.
+     * {@link #KEYGUARD_DISABLE_FACE}, {@link #KEYGUARD_DISABLE_IRIS} and
+     * {@link #KEYGUARD_DISABLE_SECURE_CAMERA} can also be set on the {@link DevicePolicyManager}
+     * instance returned by {@link #getParentProfileInstance(ComponentName)} in order to set
+     * restrictions on the parent profile. {@link #KEYGUARD_DISABLE_SECURE_CAMERA} can only be set
+     * on the parent profile instance if the calling device admin is the profile owner of an
+     * organization-owned managed profile.
      * <p>
      * Requests to disable other features on a managed profile will be ignored.
      * <p>
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ea66fd47..db4f1de 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -90,6 +90,7 @@
      * The name of the dialer role.
      *
      * @see Intent#ACTION_DIAL
+     * @see android.telecom.InCallService
      */
     public static final String ROLE_DIALER = "android.app.role.DIALER";
 
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index ab71e73..0f999ad 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -41,6 +41,43 @@
     /** @hide */
     public static final String INSTANT_APP_CLASS_NAME = "android.instant_class";
 
+    /** @hide */
+    public static final String OBFUSCATED_NOTIFICATION_CHANNEL_ID = "unknown_channel_id";
+
+    /**
+     * Flag: indicates to not obfuscate or hide any usage event data when being queried.
+     * @hide
+     */
+    public static final int SHOW_ALL_EVENT_DATA = 0x00000000;
+
+    /**
+     * Flag: indicates to obfuscate package and class names for instant apps when querying usage
+     * events.
+     * @hide
+     */
+    public static final int OBFUSCATE_INSTANT_APPS = 0x00000001;
+
+    /**
+     * Flag: indicates to hide all {@link Event#SHORTCUT_INVOCATION} events when querying usage
+     * events.
+     * @hide
+     */
+    public static final int HIDE_SHORTCUT_EVENTS = 0x00000002;
+
+    /**
+     * Flag: indicates to obfuscate the notification channel id for all notification events,
+     * such as {@link Event#NOTIFICATION_SEEN} and {@link Event#NOTIFICATION_INTERRUPTION} events,
+     * when querying usage events.
+     * @hide
+     */
+    public static final int OBFUSCATE_NOTIFICATION_EVENTS = 0x00000004;
+
+    /**
+     * Flag: indicates to hide all {@link Event#LOCUS_ID_SET} events when querying usage events.
+     * @hide
+     */
+    public static final int HIDE_LOCUS_EVENTS = 0x00000008;
+
     /**
      * An event representing a state change for a component.
      */
@@ -627,6 +664,13 @@
             return ret;
         }
 
+        /** @hide */
+        public Event getObfuscatedNotificationEvent() {
+            final Event ret = new Event(this);
+            ret.mNotificationChannelId = OBFUSCATED_NOTIFICATION_CHANNEL_ID;
+            return ret;
+        }
+
         /**
          * Returns the locusId for this event if the event is of type {@link #LOCUS_ID_SET},
          * otherwise it returns null.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5668944..2c701b4 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -599,7 +599,8 @@
     /**
      * Returns whether the specified app is currently considered inactive. This will be true if the
      * app hasn't been used directly or indirectly for a period of time defined by the system. This
-     * could be of the order of several hours or days.
+     * could be of the order of several hours or days. Apps are not considered inactive when the
+     * device is charging.
      * @param packageName The package name of the app to query
      * @return whether the app is currently considered inactive
      */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 49f62f4..ae12de0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4064,16 +4064,16 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link android.net.wifi.WifiCondManager} for handling management of the Wi-Fi control
-     * daemon.
+     * {@link android.net.wifi.wificond.WifiNl80211Manager} for handling management of the
+     * Wi-Fi nl802.11 daemon (wificond).
      *
      * @see #getSystemService(String)
-     * @see android.net.wifi.WifiCondManager
+     * @see android.net.wifi.wificond.WifiNl80211Manager
      * @hide
      */
     @SystemApi
     @SuppressLint("ServiceName")
-    public static final String WIFI_COND_SERVICE = "wificond";
+    public static final String WIFI_NL80211_SERVICE = "wifinl80211";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
@@ -5098,10 +5098,11 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
-     * AppSearchManager for indexing and querying app data managed
-     * by the system.
+     * {@link android.app.appsearch.AppSearchManager} for
+     * indexing and querying app data managed by the system.
      *
      * @see #getSystemService(String)
+     * @hide
      */
     public static final String APP_SEARCH_SERVICE = "app_search";
 
diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
index 475f019..9d37299 100644
--- a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
+++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
@@ -16,6 +16,10 @@
 
 package android.content.integrity;
 
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import java.util.Map;
 
 /**
@@ -25,7 +29,29 @@
  *
  * @hide
  */
-public class InstallerAllowedByManifestFormula extends IntegrityFormula {
+public class InstallerAllowedByManifestFormula extends IntegrityFormula implements Parcelable {
+
+    public static final String INSTALLER_CERTIFICATE_NOT_EVALUATED = "";
+
+    public InstallerAllowedByManifestFormula() {
+    }
+
+    private InstallerAllowedByManifestFormula(Parcel in) {
+    }
+
+    @NonNull
+    public static final Creator<InstallerAllowedByManifestFormula> CREATOR =
+            new Creator<InstallerAllowedByManifestFormula>() {
+                @Override
+                public InstallerAllowedByManifestFormula createFromParcel(Parcel in) {
+                    return new InstallerAllowedByManifestFormula(in);
+                }
+
+                @Override
+                public InstallerAllowedByManifestFormula[] newArray(int size) {
+                    return new InstallerAllowedByManifestFormula[size];
+                }
+            };
 
     @Override
     public int getTag() {
@@ -54,10 +80,30 @@
     private static boolean installerInAllowedInstallersFromManifest(
             AppInstallMetadata appInstallMetadata,
             Map<String, String> allowedInstallersAndCertificates) {
-        return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName())
-                && appInstallMetadata.getInstallerCertificates()
-                .contains(
-                        allowedInstallersAndCertificates
-                        .get(appInstallMetadata.getInstallerName()));
+        String installerPackage = appInstallMetadata.getInstallerName();
+
+        if (!allowedInstallersAndCertificates.containsKey(installerPackage)) {
+            return false;
+        }
+
+        // If certificate is not specified in the manifest, we do not check it.
+        if (!allowedInstallersAndCertificates.get(installerPackage)
+                .equals(INSTALLER_CERTIFICATE_NOT_EVALUATED)) {
+            return appInstallMetadata.getInstallerCertificates()
+                    .contains(
+                            allowedInstallersAndCertificates
+                                    .get(appInstallMetadata.getInstallerName()));
+        }
+
+        return true;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
     }
 }
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
index ac4c907..c5e5c8a 100644
--- a/core/java/android/content/integrity/IntegrityFormula.java
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -214,6 +214,8 @@
                 return LongAtomicFormula.CREATOR.createFromParcel(in);
             case BOOLEAN_ATOMIC_FORMULA_TAG:
                 return BooleanAtomicFormula.CREATOR.createFromParcel(in);
+            case INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG:
+                return InstallerAllowedByManifestFormula.CREATOR.createFromParcel(in);
             default:
                 throw new IllegalArgumentException("Unknown formula tag " + tag);
         }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d251ba9..9d1c677 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -702,6 +702,13 @@
      */
     public static final int PRIVATE_FLAG_ODM = 1 << 30;
 
+    /**
+     * Value for {@link #privateFlags}: If {@code true} this app allows heap tagging.
+     * {@link com.android.server.am.ProcessList#NATIVE_HEAP_POINTER_TAGGING}
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1 << 31;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -733,6 +740,7 @@
             PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
             PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE,
             PRIVATE_FLAG_ODM,
+            PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationInfoPrivateFlags {}
@@ -1878,6 +1886,15 @@
         return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;
     }
 
+    /**
+     * If {@code true} this app allows heap pointer tagging.
+     *
+     * @hide
+     */
+    public boolean allowsNativeHeapPointerTagging() {
+        return (privateFlags & PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) != 0;
+    }
+
     private boolean isAllowedToUseHiddenApis() {
         if (isSignedWithPlatformKey()) {
             return true;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c78d30d..ae14748 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -38,7 +38,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.StorageStatsManager;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -3385,6 +3385,14 @@
     public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18;
 
     /**
+     * Permission flag: Whether permission was revoked by auto-revoke.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 20;
+
+    /**
      * Permission flags: Reserved for use by the permission controller.
      *
      * @hide
@@ -3437,7 +3445,8 @@
             | FLAG_PERMISSION_REVOKED_COMPAT
             | FLAG_PERMISSION_ONE_TIME
             | FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED
-            | FLAG_PERMISSION_AUTO_REVOKE_USER_SET;
+            | FLAG_PERMISSION_AUTO_REVOKE_USER_SET
+            | FLAG_PERMISSION_AUTO_REVOKED;
 
     /**
      * Injected activity in app that forwards user to setting activity of that app.
@@ -3581,7 +3590,7 @@
      * @hide
      */
     @ChangeId
-    @Disabled
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     public static final long FILTER_APPLICATION_QUERY = 135549675L;
 
     /** {@hide} */
@@ -4262,7 +4271,8 @@
             FLAG_PERMISSION_REVOKED_COMPAT,
             FLAG_PERMISSION_ONE_TIME,
             FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED,
-            FLAG_PERMISSION_AUTO_REVOKE_USER_SET
+            FLAG_PERMISSION_AUTO_REVOKE_USER_SET,
+            FLAG_PERMISSION_AUTO_REVOKED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PermissionFlags {}
@@ -7401,6 +7411,7 @@
             case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
             case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED";
             case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET";
+            case FLAG_PERMISSION_AUTO_REVOKED: return "AUTO_REVOKED";
             default: return Integer.toString(flag);
         }
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 637e64d..da44f70 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3710,6 +3710,11 @@
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
         }
 
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING;
+        }
+
         ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
         ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
 
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 548d82a..a267113 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2098,6 +2098,9 @@
                     R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
                     parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q));
 
+            parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
+
             parsingPackage
                     .setMaxAspectRatio(
                             sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 0df9500..778d7b8 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -1509,6 +1509,16 @@
     }
 
     @Override
+    public PackageImpl setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging) {
+        this.privateFlags = allowNativeHeapPointerTagging
+                ? this.privateFlags | ApplicationInfo
+                        .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
+                : this.privateFlags & ~ApplicationInfo
+                        .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING;
+        return this;
+    }
+
+    @Override
     public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
         this.privateFlags = usesNonSdkApi
                 ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index a2fe064..954d65c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -191,6 +191,8 @@
 
     ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
 
+    ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+
     ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
 
     ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 471e83c..cb809da 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -62,6 +62,7 @@
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
@@ -112,7 +113,7 @@
     static final String TAG = "Resources";
 
     private static final Object sSync = new Object();
-    private final Object mLock = new Object();
+    private final Object mUpdateLock = new Object();
 
     // Used by BridgeResources in layoutlib
     @UnsupportedAppUsage
@@ -139,6 +140,7 @@
     @UnsupportedAppUsage
     final ClassLoader mClassLoader;
 
+    @GuardedBy("mUpdateLock")
     private UpdateCallbacks mCallbacks = null;
 
     /**
@@ -2375,6 +2377,7 @@
      *
      * <p>Loaders are listed in increasing precedence order. A loader will override the resources
      * and assets of loaders listed before itself.
+     * @hide
      */
     @NonNull
     public List<ResourcesLoader> getLoaders() {
@@ -2382,87 +2385,81 @@
     }
 
     /**
-     * Appends a loader to the end of the loader list. If the loader is already present in the
-     * loader list, the list will not be modified.
-     *
-     * @param loader the loader to add
-     */
-    public void addLoader(@NonNull ResourcesLoader loader) {
-        synchronized (mLock) {
-            checkCallbacksRegistered();
-
-            final List<ResourcesLoader> loaders = new ArrayList<>(
-                    mResourcesImpl.getAssets().getLoaders());
-            if (loaders.contains(loader)) {
-                return;
-            }
-
-            loaders.add(loader);
-            mCallbacks.onLoadersChanged(this, loaders);
-            loader.registerOnProvidersChangedCallback(this, mCallbacks);
-        }
-    }
-
-    /**
-     * Removes a loader from the loaders. If the loader is not present in the loader list, the list
+     * Adds a loader to the list of loaders. If the loader is already present in the list, the list
      * will not be modified.
      *
-     * @param loader the loader to remove
+     * @param loaders the loaders to add
      */
-    public void removeLoader(@NonNull ResourcesLoader loader) {
-        synchronized (mLock) {
+    public void addLoaders(@NonNull ResourcesLoader... loaders) {
+        synchronized (mUpdateLock) {
             checkCallbacksRegistered();
+            final List<ResourcesLoader> newLoaders =
+                    new ArrayList<>(mResourcesImpl.getAssets().getLoaders());
+            final ArraySet<ResourcesLoader> loaderSet = new ArraySet<>(newLoaders);
 
-            final List<ResourcesLoader> loaders = new ArrayList<>(
-                    mResourcesImpl.getAssets().getLoaders());
-            if (!loaders.remove(loader)) {
+            for (int i = 0; i < loaders.length; i++) {
+                final ResourcesLoader loader = loaders[i];
+                if (!loaderSet.contains(loader)) {
+                    newLoaders.add(loader);
+                }
+            }
+
+            if (loaderSet.size() == newLoaders.size()) {
                 return;
             }
 
-            mCallbacks.onLoadersChanged(this, loaders);
-            loader.unregisterOnProvidersChangedCallback(this);
+            mCallbacks.onLoadersChanged(this, newLoaders);
+            for (int i = loaderSet.size(), n = newLoaders.size(); i < n; i++) {
+                newLoaders.get(i).registerOnProvidersChangedCallback(this, mCallbacks);
+            }
         }
     }
 
     /**
-     * Sets the list of loaders.
+     * Removes loaders from the list of loaders. If the loader is not present in the list, the list
+     * will not be modified.
      *
-     * @param loaders the new loaders
+     * @param loaders the loaders to remove
      */
-    public void setLoaders(@NonNull List<ResourcesLoader> loaders) {
-        synchronized (mLock) {
+    public void removeLoaders(@NonNull ResourcesLoader... loaders) {
+        synchronized (mUpdateLock) {
             checkCallbacksRegistered();
-
+            final ArraySet<ResourcesLoader> removedLoaders = new ArraySet<>(loaders);
+            final List<ResourcesLoader> newLoaders = new ArrayList<>();
             final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders();
-            int index = 0;
-            boolean modified = loaders.size() != oldLoaders.size();
-            final ArraySet<ResourcesLoader> seenLoaders = new ArraySet<>();
-            for (final ResourcesLoader loader : loaders) {
-                if (!seenLoaders.add(loader)) {
-                    throw new IllegalArgumentException("Loader " + loader + " present twice");
-                }
 
-                if (!modified && oldLoaders.get(index++) != loader) {
-                    modified = true;
+            for (int i = 0, n = oldLoaders.size(); i < n; i++) {
+                final ResourcesLoader loader = oldLoaders.get(i);
+                if (!removedLoaders.contains(loader)) {
+                    newLoaders.add(loader);
                 }
             }
 
-            if (!modified) {
+            if (oldLoaders.size() == newLoaders.size()) {
                 return;
             }
 
-            mCallbacks.onLoadersChanged(this, loaders);
-            for (int i = 0, n = oldLoaders.size(); i < n; i++) {
-                oldLoaders.get(i).unregisterOnProvidersChangedCallback(this);
-            }
-            for (ResourcesLoader newLoader : loaders) {
-                newLoader.registerOnProvidersChangedCallback(this, mCallbacks);
+            mCallbacks.onLoadersChanged(this, newLoaders);
+            for (int i = 0; i < loaders.length; i++) {
+                loaders[i].unregisterOnProvidersChangedCallback(this);
             }
         }
     }
 
-    /** Removes all {@link ResourcesLoader ResourcesLoader(s)}. */
+    /**
+     * Removes all {@link ResourcesLoader ResourcesLoader(s)}.
+     * @hide
+     */
+    @VisibleForTesting
     public void clearLoaders() {
-        setLoaders(Collections.emptyList());
+        synchronized (mUpdateLock) {
+            checkCallbacksRegistered();
+            final List<ResourcesLoader> newLoaders = Collections.emptyList();
+            final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders();
+            mCallbacks.onLoadersChanged(this, newLoaders);
+            for (ResourcesLoader loader : oldLoaders) {
+                loader.unregisterOnProvidersChangedCallback(this);
+            }
+        }
     }
 }
diff --git a/core/java/android/content/res/loader/ResourcesLoader.java b/core/java/android/content/res/loader/ResourcesLoader.java
index 69dacee..58fec60 100644
--- a/core/java/android/content/res/loader/ResourcesLoader.java
+++ b/core/java/android/content/res/loader/ResourcesLoader.java
@@ -40,8 +40,8 @@
  * of {@link ResourcesProvider ResourcesProvider(s)} a loader contains propagates to all Resources
  * objects that use the loader.
  *
- * <p>Loaders retrieved with {@link Resources#getLoaders()} are listed in increasing precedence
- * order. A loader will override the resources and assets of loaders listed before itself.
+ * <p>Loaders must be added to Resources objects in increasing precedence order. A loader will
+ * override the resources and assets of loaders added before itself.
  *
  * <p>Providers retrieved with {@link #getProviders()} are listed in increasing precedence order. A
  * provider will override the resources and assets of providers listed before itself.
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index ef28e6c..ac36188 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -75,6 +75,18 @@
     /**
      * @hide
      */
+    public static final String KEY_DEVICE_CREDENTIAL_TITLE = "device_credential_title";
+    /**
+     * @hide
+     */
+    public static final String KEY_DEVICE_CREDENTIAL_SUBTITLE = "device_credential_subtitle";
+    /**
+     * @hide
+     */
+    public static final String KEY_DEVICE_CREDENTIAL_DESCRIPTION = "device_credential_description";
+    /**
+     * @hide
+     */
     public static final String KEY_NEGATIVE_TEXT = "negative_text";
     /**
      * @hide
@@ -221,6 +233,30 @@
         }
 
         /**
+         * Sets an optional title, subtitle, and/or description that will override other text when
+         * the user is authenticating with PIN/pattern/password. Currently for internal use only.
+         * @return This builder.
+         * @hide
+         */
+        @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+        @NonNull
+        public Builder setTextForDeviceCredential(
+                @Nullable CharSequence title,
+                @Nullable CharSequence subtitle,
+                @Nullable CharSequence description) {
+            if (title != null) {
+                mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_TITLE, title);
+            }
+            if (subtitle != null) {
+                mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_SUBTITLE, subtitle);
+            }
+            if (description != null) {
+                mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_DESCRIPTION, description);
+            }
+            return this;
+        }
+
+        /**
          * Required: Sets the text, executor, and click listener for the negative button on the
          * prompt. This is typically a cancel button, but may be also used to show an alternative
          * method for authentication, such as a screen that asks for a backup password.
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
new file mode 100644
index 0000000..4447197
--- /dev/null
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 09ec6c3..d83715c 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -51,7 +51,7 @@
  *
  * <p>Note that not all aspects of IPsec are permitted by this API. Applications may create
  * transport mode security associations and apply them to individual sockets. Applications looking
- * to create a VPN should use {@link VpnService}.
+ * to create an IPsec VPN should use {@link VpnManager} and {@link Ikev2VpnProfile}.
  *
  * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
  *     Internet Protocol</a>
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 6450a67..6516917 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -16,6 +16,7 @@
 
 package android.os.incremental;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -47,6 +48,15 @@
     }
 
     /**
+     * Construct a V4Signature from .idsig file.
+     */
+    public static V4Signature readFrom(byte[] bytes) throws IOException {
+        try (DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes))) {
+            return readFrom(stream);
+        }
+    }
+
+    /**
      * Store the V4Signature to a byte-array.
      */
     public byte[] toByteArray() {
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e65bd9f..d273500 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -301,6 +301,13 @@
     public static final int LISTEN_USER_MOBILE_DATA_STATE                  = 0x00080000;
 
     /**
+     *  Listen for display info changed event.
+     *
+     *  @see #onDisplayInfoChanged
+     */
+    public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
+
+    /**
      *  Listen for changes to the phone capability.
      *
      *  @see #onPhoneCapabilityChanged
@@ -848,6 +855,21 @@
     }
 
     /**
+     * Callback invoked when the display info has changed on the registered subscription.
+     * <p> The {@link DisplayInfo} contains status information shown to the user based on
+     * carrier policy.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param displayInfo The display information.
+     */
+    @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE))
+    public void onDisplayInfoChanged(@NonNull DisplayInfo displayInfo) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when the current emergency number list has changed on the registered
      * subscription.
      * Note, the registration subId comes from {@link TelephonyManager} object which registers
@@ -1226,6 +1248,15 @@
                             () -> psl.onUserMobileDataStateChanged(enabled)));
         }
 
+        public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDisplayInfoChanged(displayInfo)));
+        }
+
         public void onOemHookRawEvent(byte[] rawData) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4024db1..2c077bb 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -589,6 +589,24 @@
     }
 
     /**
+     * Notify display info changed.
+     *
+     * @param slotIndex The SIM slot index for which display info has changed. Can be
+     * derived from {@code subscriptionId} except when {@code subscriptionId} is invalid, such as
+     * when the device is in emergency-only mode.
+     * @param subscriptionId Subscription id for which display network info has changed.
+     * @param displayInfo The display info.
+     */
+    public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId,
+                                         @NonNull DisplayInfo displayInfo) {
+        try {
+            sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, displayInfo);
+        } catch (RemoteException ex) {
+            // system process is dead
+        }
+    }
+
+    /**
      * Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}.
      *
      * @param subId for which ims call disconnect.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2548068..9cd6050e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -882,6 +882,9 @@
         } else {
             hideDirectly(types);
         }
+        if (mViewRoot.mView == null) {
+            return;
+        }
         mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
         mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
             @Override
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 06cb519..0c1edac 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -19,7 +19,6 @@
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
 import android.graphics.RecordingCanvas;
@@ -109,12 +108,10 @@
     private long mStartDelay = 0;
     private long mStartTime;
 
-    @UnsupportedAppUsage
     public static int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
 
-    @UnsupportedAppUsage
     public RenderNodeAnimator(int property, float finalValue) {
         mRenderProperty = property;
         mFinalValue = finalValue;
@@ -122,7 +119,6 @@
         init(nCreateAnimator(property, finalValue));
     }
 
-    @UnsupportedAppUsage
     public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
         init(nCreateCanvasPropertyFloatAnimator(
                 property.getNativeContainer(), finalValue));
@@ -137,7 +133,6 @@
      *            {@link #PAINT_STROKE_WIDTH}
      * @param finalValue The target value for the property
      */
-    @UnsupportedAppUsage
     public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
         init(nCreateCanvasPropertyPaintAnimator(
                 property.getNativeContainer(), paintField, finalValue));
@@ -289,7 +284,6 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
     public void setTarget(View view) {
         mViewTarget = view;
         setTarget(mViewTarget.mRenderNode);
@@ -301,7 +295,6 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
     public void setTarget(DisplayListCanvas canvas) {
         setTarget((RecordingCanvas) canvas);
     }
@@ -316,7 +309,6 @@
         mTarget.addAnimator(this);
     }
 
-    @UnsupportedAppUsage
     public void setStartValue(float startValue) {
         checkMutable();
         nSetStartValue(mNativePtr.get(), startValue);
@@ -337,7 +329,6 @@
         return mUnscaledStartDelay;
     }
 
-    @UnsupportedAppUsage
     @Override
     public RenderNodeAnimator setDuration(long duration) {
         checkMutable();
@@ -502,7 +493,6 @@
     }
 
     // Called by native
-    @UnsupportedAppUsage
     private static void callOnFinished(RenderNodeAnimator animator) {
         if (animator.mHandler != null) {
             animator.mHandler.post(animator::onFinished);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index bf84819..680a878 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
 
 /**
  * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
@@ -40,6 +41,7 @@
     private WindowlessWindowManager mWm;
 
     private SurfaceControl mSurfaceControl;
+    private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
     /**
      * Package encapsulating a Surface hierarchy which contains interactive view
@@ -49,15 +51,18 @@
      */
     public static final class SurfacePackage implements Parcelable {
         private final SurfaceControl mSurfaceControl;
-        // TODO: Accessibility ID goes here
+        private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
-        SurfacePackage(SurfaceControl sc) {
+        SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) {
             mSurfaceControl = sc;
+            mAccessibilityEmbeddedConnection = connection;
         }
 
         private SurfacePackage(Parcel in) {
             mSurfaceControl = new SurfaceControl();
             mSurfaceControl.readFromParcel(in);
+            mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
+                    in.readStrongBinder());
         }
 
         /**
@@ -69,6 +74,16 @@
             return mSurfaceControl;
         }
 
+        /**
+         * Gets an accessibility embedded connection interface for this SurfaceControlViewHost.
+         *
+         * @return {@link IAccessibilityEmbeddedConnection} interface.
+         * @hide
+         */
+        public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
+            return mAccessibilityEmbeddedConnection;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -77,6 +92,7 @@
         @Override
         public void writeToParcel(@NonNull Parcel out, int flags) {
             mSurfaceControl.writeToParcel(out, flags);
+            out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
         }
 
         public static final @NonNull Creator<SurfacePackage> CREATOR
@@ -95,6 +111,7 @@
             @NonNull WindowlessWindowManager wwm) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm);
+        mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
     /**
@@ -118,6 +135,7 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
         mViewRoot = new ViewRootImpl(context, display, mWm);
+        mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
     /**
@@ -128,8 +146,8 @@
      * are linked.
      */
     public @Nullable SurfacePackage getSurfacePackage() {
-        if (mSurfaceControl != null) {
-            return new SurfacePackage(mSurfaceControl);
+        if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
+            return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection);
         } else {
             return null;
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d5ed36b..47ffd3e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -28,6 +28,7 @@
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -38,12 +39,14 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceControlViewHost;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
 
 import com.android.internal.view.SurfaceCallbackHelper;
 
@@ -203,8 +206,12 @@
     private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
     private int mParentSurfaceGenerationId;
 
-    // The token of embedded windowless view hierarchy.
-    private IBinder mEmbeddedViewHierarchy;
+    private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+
+    private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+    private final Matrix mTmpMatrix = new Matrix();
+    private final float[] mMatrixValues = new float[9];
+
     SurfaceControlViewHost.SurfacePackage mSurfacePackage;
 
     public SurfaceView(Context context) {
@@ -923,6 +930,7 @@
                     }
 
                     mTmpTransaction.apply();
+                    updateScreenMatrixForEmbeddedHierarchy();
 
                     if (sizeChanged || creating) {
                         redrawNeeded = true;
@@ -1510,6 +1518,7 @@
     @Override
     public void surfaceDestroyed() {
         setWindowStopped(true);
+        setRemoteAccessibilityEmbeddedConnection(null, null);
     }
 
     /**
@@ -1568,31 +1577,133 @@
 
     private void reparentSurfacePackage(SurfaceControl.Transaction t,
             SurfaceControlViewHost.SurfacePackage p) {
-        // TODO: Link accessibility IDs here.
+        initEmbeddedHierarchyForAccessibility(p);
         final SurfaceControl sc = p.getSurfaceControl();
         t.reparent(sc, mSurfaceControl).show(sc);
     }
 
-    /**
-     * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
-     * hierarchy.
-     *
-     * @param token IBinder token.
-     * @hide
-     */
-    public void setEmbeddedViewHierarchy(IBinder token) {
-        mEmbeddedViewHierarchy = token;
-    }
-
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        if (mEmbeddedViewHierarchy == null) {
+        final RemoteAccessibilityEmbeddedConnection wrapper =
+                getRemoteAccessibilityEmbeddedConnection();
+        if (wrapper == null) {
             return;
         }
         // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
         // leashed child would return the root node in the embedded hierarchy
-        info.addChild(mEmbeddedViewHierarchy);
+        info.addChild(wrapper.getLeashToken());
+    }
+
+    private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
+        final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
+        final RemoteAccessibilityEmbeddedConnection wrapper =
+                getRemoteAccessibilityEmbeddedConnection();
+
+        // Do nothing if package is embedding the same view hierarchy.
+        if (wrapper != null && wrapper.getConnection().equals(connection)) {
+            return;
+        }
+
+        // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
+        setRemoteAccessibilityEmbeddedConnection(null, null);
+
+        try {
+            final IBinder leashToken = connection.associateEmbeddedHierarchy(
+                    getViewRootImpl().mLeashToken, getAccessibilityViewId());
+            setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
+        }
+        updateScreenMatrixForEmbeddedHierarchy();
+    }
+
+    private void setRemoteAccessibilityEmbeddedConnection(
+            IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+        try {
+            if (mRemoteAccessibilityEmbeddedConnection != null) {
+                mRemoteAccessibilityEmbeddedConnection.getConnection()
+                        .disassociateEmbeddedHierarchy();
+                mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
+                mRemoteAccessibilityEmbeddedConnection = null;
+            }
+            if (connection != null && leashToken != null) {
+                mRemoteAccessibilityEmbeddedConnection =
+                        new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+                mRemoteAccessibilityEmbeddedConnection.linkToDeath();
+            }
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+        }
+    }
+
+    private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+        return mRemoteAccessibilityEmbeddedConnection;
+    }
+
+    private void updateScreenMatrixForEmbeddedHierarchy() {
+        mTmpMatrix.reset();
+        mTmpMatrix.setTranslate(mScreenRect.left, mScreenRect.top);
+        mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+                mScreenRect.height() / (float) mSurfaceHeight);
+
+        // If the screen matrix is identity or doesn't change, do nothing.
+        if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
+            return;
+        }
+
+        try {
+            final RemoteAccessibilityEmbeddedConnection wrapper =
+                    getRemoteAccessibilityEmbeddedConnection();
+            if (wrapper == null) {
+                return;
+            }
+            mTmpMatrix.getValues(mMatrixValues);
+            wrapper.getConnection().setScreenMatrix(mMatrixValues);
+            mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while setScreenMatrix " + e);
+        }
+    }
+
+    /**
+     * Wrapper of accessibility embedded connection for embedded view hierarchy.
+     */
+    private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+        private final IAccessibilityEmbeddedConnection mConnection;
+        private final IBinder mLeashToken;
+
+        RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+                IBinder leashToken) {
+            mConnection = connection;
+            mLeashToken = leashToken;
+        }
+
+        IAccessibilityEmbeddedConnection getConnection() {
+            return mConnection;
+        }
+
+        IBinder getLeashToken() {
+            return mLeashToken;
+        }
+
+        void linkToDeath() throws RemoteException {
+            mConnection.asBinder().linkToDeath(this, 0);
+        }
+
+        void unlinkToDeath() {
+            mConnection.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            unlinkToDeath();
+            runOnUiThread(() -> {
+                if (mRemoteAccessibilityEmbeddedConnection == this) {
+                    mRemoteAccessibilityEmbeddedConnection = null;
+                }
+            });
+        }
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 857bc50..b971a20 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -655,7 +655,7 @@
 
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
 
-    private IAccessibilityEmbeddedConnection mEmbeddedConnection;
+    private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
     static final class SystemUiVisibilityInfo {
         int seq;
@@ -9370,11 +9370,12 @@
      * Gets an accessibility embedded connection interface for this ViewRootImpl.
      * @hide
      */
-    public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
-        if (mEmbeddedConnection == null) {
-            mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
+    public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
+        if (mAccessibilityEmbeddedConnection == null) {
+            mAccessibilityEmbeddedConnection = new AccessibilityEmbeddedConnection(
+                    ViewRootImpl.this);
         }
-        return mEmbeddedConnection;
+        return mAccessibilityEmbeddedConnection;
     }
 
     private class SendWindowContentChangedAccessibilityEvent implements Runnable {
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
new file mode 100644
index 0000000..244cc30
--- /dev/null
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 8a1a0b5..556b24c 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
-    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R;
 
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 941af6e..8790bbd 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     /** @hide */
     private static final String CHROMIUM_WEBVIEW_FACTORY =
-            "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForR";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/java/com/android/internal/inputmethod/OWNERS b/core/java/com/android/internal/inputmethod/OWNERS
new file mode 100644
index 0000000..fc0e5d4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 518911e..0e9c2c4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,8 +19,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ApplicationErrorReport;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.type.DefaultMimeMapFactory;
 import android.os.Build;
@@ -36,7 +34,6 @@
 import com.android.internal.logging.AndroidConfig;
 import com.android.server.NetworkManagementSocketTagger;
 
-import dalvik.annotation.compat.VersionCodes;
 import dalvik.system.RuntimeHooks;
 import dalvik.system.ThreadPrioritySetter;
 import dalvik.system.VMRuntime;
@@ -67,18 +64,8 @@
 
     private static volatile boolean mCrashing = false;
 
-    /**
-     * Native heap allocations will now have a non-zero tag in the most significant byte.
-     * See
-     * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>.
-     */
-    @ChangeId
-    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
-    private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
-
     private static final native void nativeFinishInit();
     private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
-    private static native void nativeDisableHeapPointerTagging();
 
     private static int Clog_e(String tag, String msg, Throwable tr) {
         return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -411,20 +398,6 @@
         if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
     }
 
-    private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
-        // Heap tagging needs to be disabled before any additional threads are created, but the
-        // AppCompat framework is not initialized enough at this point.
-        // Check if the change is enabled manually.
-        if (disabledCompatChanges != null) {
-            for (int i = 0; i < disabledCompatChanges.length; i++) {
-                if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
-                    nativeDisableHeapPointerTagging();
-                    break;
-                }
-            }
-        }
-    }
-
     protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
             String[] argv, ClassLoader classLoader) {
         // If the application calls System.exit(), terminate the process
@@ -437,8 +410,6 @@
         VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
         VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
 
-        maybeDisableHeapPointerTagging(disabledCompatChanges);
-
         final Arguments args = new Arguments(argv);
 
         // The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index dfd700f..5565864 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -122,6 +122,25 @@
      */
     public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
 
+    public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
+    /**
+     * Enable pointer tagging in this process.
+     * Tags are checked during memory deallocation, but not on access.
+     * TBI stands for Top-Byte-Ignore, an ARM CPU feature.
+     * {@link https://developer.arm.com/docs/den0024/latest/the-memory-management-unit/translation-table-configuration/virtual-address-tagging}
+     */
+    public static final int MEMORY_TAG_LEVEL_TBI = 1 << 19;
+
+    /**
+     * Enable asynchronous memory tag checks in this process.
+     */
+    public static final int MEMORY_TAG_LEVEL_ASYNC = 2 << 19;
+
+    /**
+     * Enable synchronous memory tag checks in this process.
+     */
+    public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
+
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
     /** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ae54eb2..e34aa97 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -788,6 +788,10 @@
             Zygote.applyDebuggerSystemProperty(parsedArgs);
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
+            /* Enable pointer tagging in the system server unconditionally. Hardware support for
+             * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
+            parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+
             if (shouldProfileSystemServer()) {
                 parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
             }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 0f50596..3d5dfbb 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,6 +21,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.DisplayInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
@@ -54,6 +55,7 @@
     void onOemHookRawEvent(in byte[] rawData);
     void onCarrierNetworkChange(in boolean active);
     void onUserMobileDataStateChanged(in boolean enabled);
+    void onDisplayInfoChanged(in DisplayInfo displayInfo);
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onActiveDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 47752c5..520ffc9 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -23,6 +23,7 @@
 import android.telephony.CallQuality;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
+import android.telephony.DisplayInfo;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
@@ -87,6 +88,7 @@
     void notifyOpportunisticSubscriptionInfoChanged();
     void notifyCarrierNetworkChange(in boolean active);
     void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
+    void notifyDisplayInfoChanged(int slotIndex, int subId, in DisplayInfo displayInfo);
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
     void notifyActiveDataSubIdChanged(int activeDataSubId);
     void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0b1c8b7..19f6e27 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -243,14 +243,6 @@
     gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
 }
 
-static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
-        JNIEnv* env, jobject clazz) {
-    HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
-    if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
-        ALOGE("ERROR: could not disable heap pointer tagging\n");
-    }
-}
-
 /*
  * JNI registration.
  */
@@ -262,8 +254,6 @@
              (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
             {"nativeSetExitWithoutCleanup", "(Z)V",
              (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
-            {"nativeDisableHeapPointerTagging", "()V",
-             (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
     };
     return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
         methods, NELEM(methods));
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7a9a3f8..9fbb8df 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -349,6 +349,8 @@
 enum RuntimeFlags : uint32_t {
   DEBUG_ENABLE_JDWP = 1,
   PROFILE_FROM_SHELL = 1 << 15,
+  MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
+  MEMORY_TAG_LEVEL_TBI = 1 << 19,
 };
 
 enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1627,6 +1629,16 @@
     }
   }
 
+  HeapTaggingLevel heap_tagging_level;
+  switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
+    case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
+      heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+      break;
+    default:
+      heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+  }
+  android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
+
   if (NeedsNoRandomizeWorkaround()) {
     // Work around ARM kernel ASLR lossage (http://b/5817320).
     int old_personality = personality(0xffffffff);
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index bf4cdee..03676dd 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -428,7 +428,7 @@
         (section).args = "dropbox --proto system_app_wtf"
     ];
 
-    optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_system_server_crashes = 3037 [
+    optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_system_server_crash = 3037 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "dropbox --proto system_server_crash"
     ];
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
index 34ed90a..e1af962 100644
--- a/core/proto/android/stats/mediametrics/mediametrics.proto
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -154,6 +154,8 @@
     optional int64 latency_avg = 18;
     optional int64 latency_count = 19;
     optional int64 latency_unknown = 20;
+    optional int32 queue_input_buffer_error = 21;
+    optional int32 queue_secure_input_buffer_error = 22;
 }
 
 /**
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c66261b..e231c3a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1782,6 +1782,15 @@
 
              The default value is {@code false}. -->
         <attr name="crossProfile" format="boolean" />
+
+        <!-- If {@code true} this app will receive tagged pointers to native heap allocations
+             from functions like malloc() on compatible devices. Note that this may not always
+             be respected due to policy or backwards compatibility reasons. See the
+             <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged Pointers</a>
+             document for more information on this feature.
+
+             The default value is {@code true}. -->
+        <attr name="allowNativeHeapPointerTagging" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>feature</code> tag declares a feature. A feature is a logical part of an app.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4172044..0edad3b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3017,6 +3017,7 @@
       <public name="autofillInlineSuggestionSubtitle" />
       <!-- @hide @SystemApi -->
       <public name="isAutofillInlineSuggestionTheme" />
+      <public name="allowNativeHeapPointerTagging" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt
index 9e94bdc..afe9d7f 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt
@@ -44,7 +44,7 @@
         testDir = context.filesDir.resolve("DirectoryAssetsProvider_${testName.methodName}")
         assetsProvider = DirectoryAssetsProvider(testDir)
         loader = ResourcesLoader()
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
     }
 
     @After
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt
index e3ba93d..da5092d 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt
@@ -119,7 +119,7 @@
 
         val loader = ResourcesLoader()
         loader.providers = listOf(one, two)
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
 
         assertOpenedAsset()
         inOrder(two.assetsProvider, one.assetsProvider).apply {
@@ -149,7 +149,7 @@
         val loader2 = ResourcesLoader()
         loader2.addProvider(two)
 
-        resources.loaders = listOf(loader1, loader2)
+        resources.addLoaders(loader1, loader2)
 
         assertOpenedAsset()
         inOrder(two.assetsProvider, one.assetsProvider).apply {
@@ -170,7 +170,7 @@
         val loader = ResourcesLoader()
         val one = ResourcesProvider.empty(assetsProvider1)
         val two = ResourcesProvider.empty(assetsProvider2)
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.providers = listOf(one, two)
 
         assertOpenedAsset()
@@ -186,7 +186,7 @@
         val loader = ResourcesLoader()
         val one = ResourcesProvider.empty(assetsProvider1)
         val two = ResourcesProvider.empty(assetsProvider2)
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.providers = listOf(one, two)
 
         assertOpenedAsset()
@@ -202,7 +202,7 @@
         val loader = ResourcesLoader()
         val one = ResourcesProvider.empty(assetsProvider1)
         val two = ResourcesProvider.empty(assetsProvider2)
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.providers = listOf(one, two)
 
         assertOpenedAsset()
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
index 0cc56d7..16eafcd 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
@@ -192,13 +192,13 @@
     }
 
     @Test
-    fun addMultipleProviders() {
+    fun addProvidersRepeatedly() {
         val originalValue = getValue()
         val testOne = openOne()
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.addProvider(testOne)
         assertEquals(valueOne, getValue())
 
@@ -213,25 +213,25 @@
     }
 
     @Test
-    fun addMultipleLoaders() {
+    fun addLoadersRepeatedly() {
         val originalValue = getValue()
         val testOne = openOne()
         val testTwo = openTwo()
         val loader1 = ResourcesLoader()
         val loader2 = ResourcesLoader()
 
-        resources.addLoader(loader1)
+        resources.addLoaders(loader1)
         loader1.addProvider(testOne)
         assertEquals(valueOne, getValue())
 
-        resources.addLoader(loader2)
+        resources.addLoaders(loader2)
         loader2.addProvider(testTwo)
         assertEquals(valueTwo, getValue())
 
-        resources.removeLoader(loader1)
+        resources.removeLoaders(loader1)
         assertEquals(valueTwo, getValue())
 
-        resources.removeLoader(loader2)
+        resources.removeLoaders(loader2)
         assertEquals(originalValue, getValue())
     }
 
@@ -242,7 +242,7 @@
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.providers = listOf(testOne, testTwo)
         assertEquals(valueTwo, getValue())
 
@@ -254,20 +254,20 @@
     }
 
     @Test
-    fun setMultipleLoaders() {
+    fun addMultipleLoaders() {
         val originalValue = getValue()
         val loader1 = ResourcesLoader()
         loader1.addProvider(openOne())
         val loader2 = ResourcesLoader()
         loader2.addProvider(openTwo())
 
-        resources.loaders = listOf(loader1, loader2)
+        resources.addLoaders(loader1, loader2)
         assertEquals(valueTwo, getValue())
 
-        resources.removeLoader(loader2)
+        resources.removeLoaders(loader2)
         assertEquals(valueOne, getValue())
 
-        resources.loaders = Collections.emptyList()
+        resources.removeLoaders(loader1)
         assertEquals(originalValue, getValue())
     }
 
@@ -291,7 +291,7 @@
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.addProvider(testOne)
         loader.addProvider(testTwo)
         loader.addProvider(testOne)
@@ -308,9 +308,9 @@
         val loader2 = ResourcesLoader()
         loader2.addProvider(openTwo())
 
-        resources.addLoader(loader1)
-        resources.addLoader(loader2)
-        resources.addLoader(loader1)
+        resources.addLoaders(loader1)
+        resources.addLoaders(loader2)
+        resources.addLoaders(loader1)
 
         assertEquals(2, resources.loaders.size)
         assertEquals(resources.loaders[0], loader1)
@@ -323,7 +323,7 @@
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.addProvider(testOne)
         loader.addProvider(testTwo)
 
@@ -341,12 +341,16 @@
         val loader2 = ResourcesLoader()
         loader2.addProvider(openTwo())
 
-        resources.loaders = listOf(loader1, loader2)
-        resources.removeLoader(loader1)
-        resources.removeLoader(loader1)
+        resources.addLoaders(loader1, loader2)
+        resources.removeLoaders(loader1)
+        resources.removeLoaders(loader1)
 
         assertEquals(1, resources.loaders.size)
         assertEquals(resources.loaders[0], loader2)
+
+        resources.removeLoaders(loader2, loader2)
+
+        assertEquals(0, resources.loaders.size)
     }
 
     @Test
@@ -355,7 +359,7 @@
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.providers = listOf(testOne, testTwo)
         loader.providers = listOf(testOne, testTwo)
 
@@ -365,14 +369,14 @@
     }
 
     @Test
-    fun repeatedSetLoaders() {
+    fun repeatedAddMultipleLoaders() {
         val loader1 = ResourcesLoader()
         loader1.addProvider(openOne())
         val loader2 = ResourcesLoader()
         loader2.addProvider(openTwo())
 
-        resources.loaders = listOf(loader1, loader2)
-        resources.loaders = listOf(loader1, loader2)
+        resources.addLoaders(loader1, loader2)
+        resources.addLoaders(loader1, loader2)
 
         assertEquals(2, resources.loaders.size)
         assertEquals(resources.loaders[0], loader1)
@@ -386,7 +390,7 @@
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.addProvider(testOne)
         loader.addProvider(testTwo)
         assertEquals(valueTwo, getValue())
@@ -414,20 +418,20 @@
         val loader2 = ResourcesLoader()
         loader2.addProvider(testTwo)
 
-        resources.addLoader(loader1)
-        resources.addLoader(loader2)
+        resources.addLoaders(loader1)
+        resources.addLoaders(loader2)
         assertEquals(valueTwo, getValue())
 
-        resources.removeLoader(loader1)
+        resources.removeLoaders(loader1)
         assertEquals(valueTwo, getValue())
 
-        resources.addLoader(loader1)
+        resources.addLoaders(loader1)
         assertEquals(valueOne, getValue())
 
-        resources.removeLoader(loader2)
+        resources.removeLoaders(loader2)
         assertEquals(valueOne, getValue())
 
-        resources.removeLoader(loader1)
+        resources.removeLoaders(loader1)
         assertEquals(originalValue, getValue())
     }
 
@@ -444,10 +448,11 @@
         val loader2 = ResourcesLoader()
         loader2.providers = listOf(testThree, testFour)
 
-        resources.loaders = listOf(loader1, loader2)
+        resources.addLoaders(loader1, loader2)
         assertEquals(valueFour, getValue())
 
-        resources.loaders = listOf(loader2, loader1)
+        resources.removeLoaders(loader1)
+        resources.addLoaders(loader1)
         assertEquals(valueTwo, getValue())
 
         loader1.removeProvider(testTwo)
@@ -471,7 +476,7 @@
         val loader2 = ResourcesLoader()
         loader2.addProvider(openTwo())
 
-        resources.loaders = listOf(loader1)
+        resources.addLoaders(loader1)
         assertEquals(valueOne, getValue())
 
         // The child context should include the loaders of the original context.
@@ -479,12 +484,12 @@
         assertEquals(valueOne, getValue(childContext))
 
         // Changing the loaders of the child context should not affect the original context.
-        childContext.resources.loaders = listOf(loader1, loader2)
+        childContext.resources.addLoaders(loader2)
         assertEquals(valueOne, getValue())
         assertEquals(valueTwo, getValue(childContext))
 
         // Changing the loaders of the original context should not affect the child context.
-        resources.removeLoader(loader1)
+        resources.removeLoaders(loader1)
         assertEquals(originalValue, getValue())
         assertEquals(valueTwo, getValue(childContext))
 
@@ -506,7 +511,7 @@
         val testTwo = openTwo()
         val loader = ResourcesLoader()
 
-        resources.addLoader(loader)
+        resources.addLoaders(loader)
         loader.addProvider(testOne)
         assertEquals(valueOne, getValue())
 
@@ -527,7 +532,7 @@
         assertEquals(originalValue, getValue())
         assertEquals(originalValue, getValue(childContext2))
 
-        childContext2.resources.addLoader(loader)
+        childContext2.resources.addLoaders(loader)
         assertEquals(originalValue, getValue())
         assertEquals(valueTwo, getValue(childContext))
         assertEquals(valueTwo, getValue(childContext2))
@@ -539,7 +544,7 @@
         loader.addProvider(openOne())
 
         val applicationContext = context.applicationContext
-        applicationContext.resources.addLoader(loader)
+        applicationContext.resources.addLoaders(loader)
         assertEquals(valueOne, getValue(applicationContext))
 
         val activity = mTestActivityRule.launchActivity(Intent())
@@ -556,7 +561,7 @@
         loader2.addProvider(openTwo())
 
         val applicationContext = context.applicationContext
-        applicationContext.resources.addLoader(loader1)
+        applicationContext.resources.addLoaders(loader1)
         assertEquals(valueOne, getValue(applicationContext))
 
         var token: IBinder? = null
@@ -569,7 +574,7 @@
             assertEquals(valueOne, getValue(applicationContext))
             assertEquals(valueOne, getValue(activity))
 
-            activity.resources.addLoader(loader2)
+            activity.resources.addLoaders(loader2)
             assertEquals(valueOne, getValue(applicationContext))
             assertEquals(valueTwo, getValue(activity))
 
@@ -598,10 +603,11 @@
         loader2.addProvider(provider1)
         loader2.addProvider(openTwo())
 
-        resources.loaders = listOf(loader1, loader2)
+        resources.addLoaders(loader1, loader2)
         assertEquals(valueTwo, getValue())
 
-        resources.loaders = listOf(loader2, loader1)
+        resources.removeLoaders(loader1)
+        resources.addLoaders(loader1)
         assertEquals(valueOne, getValue())
 
         assertEquals(2, resources.assets.apkAssets.count { apkAssets -> apkAssets.isForLoader })
diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
index c897ace..693d4ca 100644
--- a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
@@ -16,15 +16,20 @@
 
 package android.content.integrity;
 
+import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableMap;
 
-import org.testng.annotations.Test;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import java.util.Arrays;
 import java.util.Collections;
 
+@RunWith(JUnit4.class)
 public class InstallerAllowedByManifestFormulaTest {
 
     private static final InstallerAllowedByManifestFormula
@@ -70,7 +75,7 @@
     }
 
     @Test
-    public void testFormulaMatches_certificateNotInManifest() {
+    public void testFormulaMatches_certificateDoesNotMatchManifest() {
         AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
                 .setInstallerName("installer1")
                 .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert"))
@@ -92,6 +97,19 @@
         assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
     }
 
+    @Test
+    public void testFormulaMatches_certificateNotSpecifiedInManifest() {
+        AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+                .setInstallerName("installer1")
+                .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert"))
+                .setAllowedInstallersAndCert(ImmutableMap.of(
+                        "installer1", INSTALLER_CERTIFICATE_NOT_EVALUATED,
+                        "installer2", "installer_cert1"
+                )).build();
+
+        assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
+    }
+
     /** Returns a builder with all fields filled with some dummy data. */
     private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
         return new AppInstallMetadata.Builder()
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index af115b1..4b95e4d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -381,6 +381,8 @@
         <permission name="android.permission.REBOOT"/>
         <!-- Permission required for access VIBRATOR_STATE. -->
         <permission name="android.permission.ACCESS_VIBRATOR_STATE"/>
+        <!-- Permission required for UsageStatsTest CTS test. -->
+        <permission name="android.permission.MANAGE_NOTIFICATIONS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 06d4fbd..ce8ff7d 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -867,7 +867,8 @@
         }
     }
 
-    private ColorSpace(
+    /** @hide */
+    ColorSpace(
             @NonNull String name,
             @NonNull Model model,
             @IntRange(from = MIN_ID, to = MAX_ID) int id) {
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
new file mode 100644
index 0000000..f9033a5
--- /dev/null
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -0,0 +1,183 @@
+/*
+ * 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.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A {@link Parcelable} {@link ColorSpace}. In order to enable parceling, the ColorSpace
+ * must be either a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} instance
+ * that has an ICC parametric transfer function as returned by {@link Rgb#getTransferParameters()}.
+ * TODO: Make public
+ * @hide
+ */
+public final class ParcelableColorSpace extends ColorSpace implements Parcelable {
+    private final ColorSpace mColorSpace;
+
+    /**
+     * Checks if the given ColorSpace is able to be parceled. A ColorSpace can only be
+     * parceled if it is a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb}
+     * instance that has an ICC parametric transfer function as returned by
+     * {@link Rgb#getTransferParameters()}
+     */
+    public static boolean isParcelable(@NonNull ColorSpace colorSpace) {
+        if (colorSpace.getId() == ColorSpace.MIN_ID) {
+            if (!(colorSpace instanceof ColorSpace.Rgb)) {
+                return false;
+            }
+            ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace;
+            if (rgb.getTransferParameters() == null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Constructs a new ParcelableColorSpace that wraps the provided ColorSpace.
+     *
+     * @param colorSpace The ColorSpace to wrap. The ColorSpace must be either named or be an
+     *                   RGB ColorSpace with an ICC parametric transfer function.
+     * @throws IllegalArgumentException If the provided ColorSpace does not satisfy the requirements
+     * to be parceled. See {@link #isParcelable(ColorSpace)}.
+     */
+    public ParcelableColorSpace(@NonNull ColorSpace colorSpace) {
+        super(colorSpace.getName(), colorSpace.getModel(), colorSpace.getId());
+        mColorSpace = colorSpace;
+
+        if (mColorSpace.getId() == ColorSpace.MIN_ID) {
+            if (!(mColorSpace instanceof ColorSpace.Rgb)) {
+                throw new IllegalArgumentException(
+                        "Unable to parcel unknown ColorSpaces that are not ColorSpace.Rgb");
+            }
+            ColorSpace.Rgb rgb = (ColorSpace.Rgb) mColorSpace;
+            if (rgb.getTransferParameters() == null) {
+                throw new IllegalArgumentException("ColorSpace must use an ICC "
+                        + "parametric transfer function to be parcelable");
+            }
+        }
+    }
+
+    public @NonNull ColorSpace getColorSpace() {
+        return mColorSpace;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        final int id = mColorSpace.getId();
+        dest.writeInt(id);
+        if (id == ColorSpace.MIN_ID) {
+            // Not a named color space. We have to actually write, like, stuff. And things. Ugh.
+            // Cast is safe because this was asserted in the constructor
+            ColorSpace.Rgb rgb = (ColorSpace.Rgb) mColorSpace;
+            dest.writeString(rgb.getName());
+            dest.writeFloatArray(rgb.getPrimaries());
+            dest.writeFloatArray(rgb.getWhitePoint());
+            ColorSpace.Rgb.TransferParameters transferParameters = rgb.getTransferParameters();
+            dest.writeDouble(transferParameters.a);
+            dest.writeDouble(transferParameters.b);
+            dest.writeDouble(transferParameters.c);
+            dest.writeDouble(transferParameters.d);
+            dest.writeDouble(transferParameters.e);
+            dest.writeDouble(transferParameters.f);
+            dest.writeDouble(transferParameters.g);
+        }
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParcelableColorSpace> CREATOR =
+            new Parcelable.Creator<ParcelableColorSpace>() {
+
+        public @NonNull ParcelableColorSpace createFromParcel(@NonNull Parcel in) {
+            final int id = in.readInt();
+            if (id == ColorSpace.MIN_ID) {
+                String name = in.readString();
+                float[] primaries = in.createFloatArray();
+                float[] whitePoint = in.createFloatArray();
+                double a = in.readDouble();
+                double b = in.readDouble();
+                double c = in.readDouble();
+                double d = in.readDouble();
+                double e = in.readDouble();
+                double f = in.readDouble();
+                double g = in.readDouble();
+                ColorSpace.Rgb.TransferParameters function =
+                        new ColorSpace.Rgb.TransferParameters(a, b, c, d, e, f, g);
+                return new ParcelableColorSpace(
+                        new ColorSpace.Rgb(name, primaries, whitePoint, function));
+            } else {
+                return new ParcelableColorSpace(ColorSpace.get(id));
+            }
+        }
+
+        public ParcelableColorSpace[] newArray(int size) {
+            return new ParcelableColorSpace[size];
+        }
+    };
+
+    @Override
+    public boolean isWideGamut() {
+        return mColorSpace.isWideGamut();
+    }
+
+    @Override
+    public float getMinValue(int component) {
+        return mColorSpace.getMinValue(component);
+    }
+
+    @Override
+    public float getMaxValue(int component) {
+        return mColorSpace.getMaxValue(component);
+    }
+
+    @Override
+    public @NonNull float[] toXyz(@NonNull float[] v) {
+        return mColorSpace.toXyz(v);
+    }
+
+    @Override
+    public @NonNull float[] fromXyz(@NonNull float[] v) {
+        return mColorSpace.fromXyz(v);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ParcelableColorSpace other = (ParcelableColorSpace) o;
+        return mColorSpace.equals(other.mColorSpace);
+    }
+
+    @Override
+    public int hashCode() {
+        return mColorSpace.hashCode();
+    }
+
+    /** @hide */
+    @Override
+    long getNativeInstance() {
+        return mColorSpace.getNativeInstance();
+    }
+}
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 89a9b99..84c07d7 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -16,16 +16,16 @@
 
 #include "Readback.h"
 
-#include "pipeline/skia/LayerDrawable.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/VulkanManager.h"
-
-#include <gui/Surface.h>
-#include <ui/Fence.h>
+#include <sync/sync.h>
+#include <system/window.h>
 #include <ui/GraphicBuffer.h>
+
 #include "DeferredLayerUpdater.h"
 #include "Properties.h"
 #include "hwui/Bitmap.h"
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
 #include "utils/Color.h"
 #include "utils/MathUtils.h"
 #include "utils/TraceUtils.h"
@@ -35,40 +35,43 @@
 namespace android {
 namespace uirenderer {
 
-CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
+CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap) {
     ATRACE_CALL();
     // Setup the source
-    sp<GraphicBuffer> sourceBuffer;
-    sp<Fence> sourceFence;
+    AHardwareBuffer* rawSourceBuffer;
+    int rawSourceFence;
     Matrix4 texTransform;
-    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
+    status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
+                                                     texTransform.data);
+    base::unique_fd sourceFence(rawSourceFence);
     texTransform.invalidateType();
     if (err != NO_ERROR) {
         ALOGW("Failed to get last queued buffer, error = %d", err);
         return CopyResult::UnknownError;
     }
-    if (!sourceBuffer.get()) {
+    if (rawSourceBuffer == nullptr) {
         ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
         return CopyResult::SourceEmpty;
     }
-    if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
+
+    std::unique_ptr<AHardwareBuffer, decltype(&AHardwareBuffer_release)> sourceBuffer(
+            rawSourceBuffer, AHardwareBuffer_release);
+    AHardwareBuffer_Desc description;
+    AHardwareBuffer_describe(sourceBuffer.get(), &description);
+    if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
         ALOGW("Surface is protected, unable to copy from it");
         return CopyResult::SourceInvalid;
     }
-    err = sourceFence->wait(500 /* ms */);
-    if (err != NO_ERROR) {
+
+    if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
         ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
         return CopyResult::Timeout;
     }
-    if (!sourceBuffer.get()) {
-        return CopyResult::UnknownError;
-    }
 
-    sk_sp<SkColorSpace> colorSpace =
-            DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
-    sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
-            reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()),
-            kPremul_SkAlphaType, colorSpace);
+    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
+            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
+    sk_sp<SkImage> image =
+            SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
     return copyImageInto(image, texTransform, srcRect, bitmap);
 }
 
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index e86a813..e36f1ff 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -47,7 +47,7 @@
     /**
      * Copies the surface's most recently queued buffer into the provided bitmap.
      */
-    CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
+    CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
 
     CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index f9e401a..1e7fc71 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -317,8 +317,9 @@
 int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
                                  SkBitmap* bitmap) {
     auto& thread = RenderThread::getInstance();
+    ANativeWindow* window = surface.get();
     return static_cast<int>(thread.queue().runSync([&]() -> auto {
-        return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap);
+        return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
     }));
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 4683e1d..ab0dd2b 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -140,6 +140,10 @@
      */
     ANDROID_API void setRenderAheadDepth(int renderAhead);
 
+    // TODO: This api will need to take in an ANativeWindow instead, but the
+    // caller, ThreadedRenderer, doesn't have access to libandroid due to a
+    // circular dependency, so it can't use the JNI ANativeWindow methods. Once
+    // that is resolved then replace the surface type here.
     ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
                                            int bottom, SkBitmap* bitmap);
     ANDROID_API static void prepareToDraw(Bitmap& bitmap);
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 05fa511..2bffe8a 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -156,9 +156,7 @@
             }
             BackgroundThread.getExecutor().execute(() -> {
                 final Uri uri = scanFileQuietly(mProvider, new File(path));
-                if (mClient != null) {
-                    mClient.onScanCompleted(path, uri);
-                }
+                runCallBack(mContext, mClient, path, uri);
             });
         }
     }
@@ -187,9 +185,7 @@
                     .acquireContentProviderClient(MediaStore.AUTHORITY)) {
                 for (String path : paths) {
                     final Uri uri = scanFileQuietly(client, new File(path));
-                    if (callback != null) {
-                        callback.onScanCompleted(path, uri);
-                    }
+                    runCallBack(context, callback, path, uri);
                 }
             }
         });
@@ -206,6 +202,23 @@
         return uri;
     }
 
+    private static void runCallBack(Context context, OnScanCompletedListener callback,
+            String path, Uri uri) {
+        if (callback != null) {
+            // Ignore exceptions from callback to avoid calling app from crashing.
+            // Don't ignore exceptions for apps targeting 'R' or higher.
+            try {
+                callback.onScanCompleted(path, uri);
+            } catch (Throwable e) {
+                if (context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
+                    throw e;
+                } else {
+                    Log.w(TAG, "Ignoring exception from callback for backward compatibility", e);
+                }
+            }
+        }
+    }
+
     @Deprecated
     static class ClientProxy implements MediaScannerConnectionClient {
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9b183a3..44142e3 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -532,6 +532,8 @@
         Filter filter = nativeOpenFilter(
                 mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
         if (filter != null) {
+            filter.setMainType(mainType);
+            filter.setSubtype(subType);
             filter.setCallback(cb);
             if (mHandler == null) {
                 mHandler = createEventHandler();
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 06de6e8..a98183b 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.TunerUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -177,6 +178,8 @@
     private long mNativeContext;
     private FilterCallback mCallback;
     private final int mId;
+    private int mMainType;
+    private int mSubtype;
 
     private native int nativeConfigureFilter(
             int type, int subType, FilterConfiguration settings);
@@ -197,6 +200,15 @@
     }
 
     /** @hide */
+    public void setMainType(@Type int mainType) {
+        mMainType = mainType;
+    }
+    /** @hide */
+    public void setSubtype(@Subtype int subtype) {
+        mSubtype = subtype;
+    }
+
+    /** @hide */
     public void setCallback(FilterCallback cb) {
         mCallback = cb;
     }
@@ -213,10 +225,13 @@
      */
     @Result
     public int configure(@NonNull FilterConfiguration config) {
-        int subType = -1;
+        // TODO: validate main type, subtype, config, settings
+        int subType;
         Settings s = config.getSettings();
         if (s != null) {
             subType = s.getType();
+        } else {
+            subType = TunerUtils.getFilterSubtype(mMainType, mSubtype);
         }
         return nativeConfigureFilter(config.getType(), subType, config);
     }
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index bf5aaed..a8dbfa5 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -160,6 +160,12 @@
          */
         @NonNull
         public IpFilterConfiguration build() {
+            int ipAddrLength = mSrcIpAddress.length;
+            if (ipAddrLength != mDstIpAddress.length || (ipAddrLength != 4 && ipAddrLength != 16)) {
+                throw new IllegalArgumentException(
+                    "The lengths of src and dst IP address must be 4 or 16 and must be the same."
+                            + "srcLength=" + ipAddrLength + ", dstLength=" + mDstIpAddress.length);
+            }
             return new IpFilterConfiguration(
                     mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
         }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 08c3f98..ac59003 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -27,16 +27,36 @@
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 using ::android::hardware::Void;
+using ::android::hardware::hidl_bitfield;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::tv::tuner::V1_0::DataFormat;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
 using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using ::android::hardware::tv::tuner::V1_0::DemuxScHevcIndex;
+using ::android::hardware::tv::tuner::V1_0::DemuxScIndex;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsIndex;
 using ::android::hardware::tv::tuner::V1_0::DvrSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
@@ -111,6 +131,9 @@
 
 static fields_t gFields;
 
+static int IP_V4_LENGTH = 4;
+static int IP_V6_LENGTH = 16;
+
 namespace android {
 /////////////// LnbCallback ///////////////////////
 LnbCallback::LnbCallback(jweak tunerObj, LnbId id) : mObject(tunerObj), mId(id) {}
@@ -1367,38 +1390,352 @@
     return NULL;
 }
 
-static DemuxFilterSettings getFilterSettings(
-        JNIEnv *env, int type, int subtype, jobject filterSettingsObj) {
+static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits");
+    jbyteArray jfilterBytes = static_cast<jbyteArray>(
+            env->GetObjectField(settings, env->GetFieldID(clazz, "mFilter", "[B")));
+    jsize size = env->GetArrayLength(jfilterBytes);
+    std::vector<uint8_t> filterBytes(size);
+    env->GetByteArrayRegion(
+            jfilterBytes, 0, size, reinterpret_cast<jbyte*>(&filterBytes[0]));
+
+    jbyteArray jmask = static_cast<jbyteArray>(
+            env->GetObjectField(settings, env->GetFieldID(clazz, "mMask", "[B")));
+    size = env->GetArrayLength(jmask);
+    std::vector<uint8_t> mask(size);
+    env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast<jbyte*>(&mask[0]));
+
+    jbyteArray jmode = static_cast<jbyteArray>(
+            env->GetObjectField(settings, env->GetFieldID(clazz, "mMode", "[B")));
+    size = env->GetArrayLength(jmode);
+    std::vector<uint8_t> mode(size);
+    env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast<jbyte*>(&mode[0]));
+
+    DemuxFilterSectionBits filterSectionBits {
+        .filter = filterBytes,
+        .mask = mask,
+        .mode = mode,
+    };
+    return filterSectionBits;
+}
+
+static DemuxFilterSectionSettings::Condition::TableInfo getFilterTableInfo(
+        JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo");
+    uint16_t tableId = static_cast<uint16_t>(
+            env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I")));
+    uint16_t version = static_cast<uint16_t>(
+            env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I")));
+    DemuxFilterSectionSettings::Condition::TableInfo tableInfo {
+        .tableId = tableId,
+        .version = version,
+    };
+    return tableInfo;
+}
+
+static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettings");
+    bool isCheckCrc = static_cast<bool>(
+            env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z")));
+    bool isRepeat = static_cast<bool>(
+            env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z")));
+    bool isRaw = static_cast<bool>(
+            env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")));
+
+    DemuxFilterSectionSettings filterSectionSettings {
+        .isCheckCrc = isCheckCrc,
+        .isRepeat = isRepeat,
+        .isRaw = isRaw,
+    };
+    if (env->IsInstanceOf(
+            settings,
+            env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) {
+        filterSectionSettings.condition.sectionBits(getFilterSectionBits(env, settings));
+    } else if (env->IsInstanceOf(
+            settings,
+            env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) {
+        filterSectionSettings.condition.tableInfo(getFilterTableInfo(env, settings));
+    }
+    return filterSectionSettings;
+}
+
+static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings");
+    bool isPassthrough = static_cast<bool>(
+            env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z")));
+    DemuxFilterAvSettings filterAvSettings {
+        .isPassthrough = isPassthrough,
+    };
+    return filterAvSettings;
+}
+
+static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings");
+    uint16_t streamId = static_cast<uint16_t>(
+            env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I")));
+    bool isRaw = static_cast<bool>(
+            env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")));
+    DemuxFilterPesDataSettings filterPesDataSettings {
+        .streamId = streamId,
+        .isRaw = isRaw,
+    };
+    return filterPesDataSettings;
+}
+
+static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/RecordSettings");
+    hidl_bitfield<DemuxTsIndex> tsIndexMask = static_cast<hidl_bitfield<DemuxTsIndex>>(
+            env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I")));
+    DemuxRecordScIndexType scIndexType = static_cast<DemuxRecordScIndexType>(
+            env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I")));
+    jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I"));
+
+    DemuxFilterRecordSettings filterRecordSettings {
+        .tsIndexMask = tsIndexMask,
+        .scIndexType = scIndexType,
+    };
+    if (scIndexType == DemuxRecordScIndexType::SC) {
+        filterRecordSettings.scIndexMask.sc(static_cast<hidl_bitfield<DemuxScIndex>>(scIndexMask));
+    } else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) {
+        filterRecordSettings.scIndexMask.scHevc(
+                static_cast<hidl_bitfield<DemuxScHevcIndex>>(scIndexMask));
+    }
+    return filterRecordSettings;
+}
+
+static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings");
+    uint32_t downloadId = static_cast<uint32_t>(
+            env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I")));
+
+    DemuxFilterDownloadSettings filterDownloadSettings {
+        .downloadId = downloadId,
+    };
+    return filterDownloadSettings;
+}
+
+static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/filter/IpFilterConfiguration");
+
+    jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
+            env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B")));
+    jsize srcSize = env->GetArrayLength(jsrcIpAddress);
+    jbyteArray jdstIpAddress = static_cast<jbyteArray>(
+            env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B")));
+    jsize dstSize = env->GetArrayLength(jdstIpAddress);
+
+    DemuxIpAddress res;
+
+    if (srcSize != dstSize) {
+        // should never happen. Validated on Java size.
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+            "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize);
+        return res;
+    }
+
+    if (srcSize == IP_V4_LENGTH) {
+        uint8_t srcAddr[IP_V4_LENGTH];
+        uint8_t dstAddr[IP_V4_LENGTH];
+        env->GetByteArrayRegion(
+                jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr));
+        env->GetByteArrayRegion(
+                jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr));
+        res.srcIpAddress.v4(srcAddr);
+        res.dstIpAddress.v4(dstAddr);
+    } else if (srcSize == IP_V6_LENGTH) {
+        uint8_t srcAddr[IP_V6_LENGTH];
+        uint8_t dstAddr[IP_V6_LENGTH];
+        env->GetByteArrayRegion(
+                jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr));
+        env->GetByteArrayRegion(
+                jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr));
+        res.srcIpAddress.v6(srcAddr);
+        res.dstIpAddress.v6(dstAddr);
+    } else {
+        // should never happen. Validated on Java size.
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+            "Invalid IP address length %d", srcSize);
+        return res;
+    }
+
+    uint16_t srcPort = static_cast<uint16_t>(
+            env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I")));
+    uint16_t dstPort = static_cast<uint16_t>(
+            env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I")));
+
+    res.srcPort = srcPort;
+    res.dstPort = dstPort;
+
+    return res;
+}
+
+static DemuxFilterSettings getFilterConfiguration(
+        JNIEnv *env, int type, int subtype, jobject filterConfigObj) {
     DemuxFilterSettings filterSettings;
-    // TODO: more setting types
     jobject settingsObj =
             env->GetObjectField(
-                    filterSettingsObj,
+                    filterConfigObj,
                     env->GetFieldID(
                             env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"),
                             "mSettings",
                             "Landroid/media/tv/tuner/filter/Settings;"));
-    if (type == (int)DemuxFilterMainType::TS) {
-        // DemuxTsFilterSettings
-        jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
-        int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I"));
-        if (subtype == (int)DemuxTsFilterType::PES) {
-            // DemuxFilterPesDataSettings
-            jclass settingClazz =
-                    env->FindClass("android/media/tv/tuner/filter/PesSettings");
-            int streamId = env->GetIntField(
-                    settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I"));
-            bool isRaw = (bool)env->GetBooleanField(
-                    settingsObj, env->GetFieldID(settingClazz, "mIsRaw", "Z"));
-            DemuxFilterPesDataSettings filterPesDataSettings {
-                    .streamId = static_cast<uint16_t>(streamId),
-                    .isRaw = isRaw,
-            };
+    DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type);
+    switch (mainType) {
+        case DemuxFilterMainType::TS: {
+            jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
+            uint16_t tpid = static_cast<uint16_t>(
+                    env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I")));
             DemuxTsFilterSettings tsFilterSettings {
-                    .tpid = static_cast<uint16_t>(tpid),
+                .tpid = tpid,
             };
-            tsFilterSettings.filterSettings.pesData(filterPesDataSettings);
+
+            DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
+            switch (tsType) {
+                case DemuxTsFilterType::SECTION:
+                    tsFilterSettings.filterSettings.section(
+                            getFilterSectionSettings(env, settingsObj));
+                    break;
+                case DemuxTsFilterType::AUDIO:
+                case DemuxTsFilterType::VIDEO:
+                    tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+                    break;
+                case DemuxTsFilterType::PES:
+                    tsFilterSettings.filterSettings.pesData(
+                            getFilterPesDataSettings(env, settingsObj));
+                    break;
+                case DemuxTsFilterType::RECORD:
+                    tsFilterSettings.filterSettings.record(
+                            getFilterRecordSettings(env, settingsObj));
+                    break;
+                default:
+                    break;
+            }
             filterSettings.ts(tsFilterSettings);
+            break;
+        }
+        case DemuxFilterMainType::MMTP: {
+            jclass clazz = env->FindClass("android/media/tv/tuner/filter/MmtpFilterConfiguration");
+            uint16_t mmtpPid = static_cast<uint16_t>(
+                    env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I")));
+            DemuxMmtpFilterSettings mmtpFilterSettings {
+                .mmtpPid = mmtpPid,
+            };
+            DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
+            switch (mmtpType) {
+                case DemuxMmtpFilterType::SECTION:
+                    mmtpFilterSettings.filterSettings.section(
+                            getFilterSectionSettings(env, settingsObj));
+                    break;
+                case DemuxMmtpFilterType::AUDIO:
+                case DemuxMmtpFilterType::VIDEO:
+                    mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+                    break;
+                case DemuxMmtpFilterType::PES:
+                    mmtpFilterSettings.filterSettings.pesData(
+                            getFilterPesDataSettings(env, settingsObj));
+                    break;
+                case DemuxMmtpFilterType::RECORD:
+                    mmtpFilterSettings.filterSettings.record(
+                            getFilterRecordSettings(env, settingsObj));
+                    break;
+                case DemuxMmtpFilterType::DOWNLOAD:
+                    mmtpFilterSettings.filterSettings.download(
+                            getFilterDownloadSettings(env, settingsObj));
+                    break;
+                default:
+                    break;
+            }
+            filterSettings.mmtp(mmtpFilterSettings);
+            break;
+        }
+        case DemuxFilterMainType::IP: {
+            DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
+
+            DemuxIpFilterSettings ipFilterSettings {
+                .ipAddr = ipAddr,
+            };
+            DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype);
+            switch (ipType) {
+                case DemuxIpFilterType::SECTION: {
+                    ipFilterSettings.filterSettings.section(
+                            getFilterSectionSettings(env, settingsObj));
+                    break;
+                }
+                case DemuxIpFilterType::IP: {
+                    jclass clazz = env->FindClass(
+                            "android/media/tv/tuner/filter/IpFilterConfiguration");
+                    bool bPassthrough = static_cast<bool>(
+                            env->GetBooleanField(
+                                    filterConfigObj, env->GetFieldID(
+                                            clazz, "mPassthrough", "Z")));
+                    ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
+            filterSettings.ip(ipFilterSettings);
+            break;
+        }
+        case DemuxFilterMainType::TLV: {
+            jclass clazz = env->FindClass("android/media/tv/tuner/filter/TlvFilterConfiguration");
+            uint8_t packetType = static_cast<uint8_t>(
+                    env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")));
+            bool isCompressedIpPacket = static_cast<bool>(
+                    env->GetBooleanField(
+                            filterConfigObj, env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z")));
+
+            DemuxTlvFilterSettings tlvFilterSettings {
+                .packetType = packetType,
+                .isCompressedIpPacket = isCompressedIpPacket,
+            };
+            DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype);
+            switch (tlvType) {
+                case DemuxTlvFilterType::SECTION: {
+                    tlvFilterSettings.filterSettings.section(
+                            getFilterSectionSettings(env, settingsObj));
+                    break;
+                }
+                case DemuxTlvFilterType::TLV: {
+                    bool bPassthrough = static_cast<bool>(
+                            env->GetBooleanField(
+                                    filterConfigObj, env->GetFieldID(
+                                            clazz, "mPassthrough", "Z")));
+                    tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
+            filterSettings.tlv(tlvFilterSettings);
+            break;
+        }
+        case DemuxFilterMainType::ALP: {
+            jclass clazz = env->FindClass("android/media/tv/tuner/filter/AlpFilterConfiguration");
+            uint8_t packetType = static_cast<uint8_t>(
+                    env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")));
+            DemuxAlpLengthType lengthType = static_cast<DemuxAlpLengthType>(
+                    env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mLengthType", "I")));
+            DemuxAlpFilterSettings alpFilterSettings {
+                .packetType = packetType,
+                .lengthType = lengthType,
+            };
+            DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
+            switch (alpType) {
+                case DemuxAlpFilterType::SECTION:
+                    alpFilterSettings.filterSettings.section(
+                            getFilterSectionSettings(env, settingsObj));
+                    break;
+                default:
+                    break;
+            }
+            filterSettings.alp(alpFilterSettings);
+            break;
+        }
+        default: {
+            break;
         }
     }
     return filterSettings;
@@ -1439,7 +1776,7 @@
         ALOGD("Failed to configure filter: filter not found");
         return (int)Result::INVALID_STATE;
     }
-    DemuxFilterSettings filterSettings = getFilterSettings(env, type, subtype, settings);
+    DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings);
     Result res = iFilterSp->configure(filterSettings);
     MQDescriptorSync<uint8_t> filterMQDesc;
     if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) {
diff --git a/media/tests/TunerTest/Android.bp b/media/tests/TunerTest/Android.bp
new file mode 100644
index 0000000..cef8791
--- /dev/null
+++ b/media/tests/TunerTest/Android.bp
@@ -0,0 +1,18 @@
+android_test {
+    name: "mediatunertest",
+
+    srcs: ["**/*.java"],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    static_libs: [
+        "android-support-test",
+        "testng"
+    ],
+
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/media/tests/TunerTest/AndroidManifest.xml b/media/tests/TunerTest/AndroidManifest.xml
new file mode 100644
index 0000000..17e9f19
--- /dev/null
+++ b/media/tests/TunerTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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
+
+          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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.mediatunertest">
+
+    <uses-permission android:name="android.permission.ACCESS_TV_TUNER" />
+
+    <application android:label="@string/app_name">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.mediatunertest"
+                     android:label="Media Tuner Tests"/>
+</manifest>
diff --git a/media/tests/TunerTest/AndroidTest.xml b/media/tests/TunerTest/AndroidTest.xml
new file mode 100644
index 0000000..d9c31f45
--- /dev/null
+++ b/media/tests/TunerTest/AndroidTest.xml
@@ -0,0 +1,17 @@
+<configuration description="Runs Media Tuner tests.">
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="MediaTunerTest"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="mediatunertest.apk"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.mediatunertest"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/media/tests/TunerTest/res/values/strings.xml b/media/tests/TunerTest/res/values/strings.xml
new file mode 100644
index 0000000..b313944
--- /dev/null
+++ b/media/tests/TunerTest/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- name of the app [CHAR LIMIT=25]-->
+    <string name="app_name">MediaTunerTest</string>
+</resources>
\ No newline at end of file
diff --git a/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
new file mode 100644
index 0000000..cbfbf77
--- /dev/null
+++ b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.mediatunertest;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.media.tv.tuner.Descrambler;
+import android.media.tv.tuner.Tuner;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TunerTest {
+    private static final String TAG = "MediaTunerTest";
+
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testTunerConstructor() throws Exception {
+        Tuner tuner = new Tuner(mContext, "123", 1, null);
+        assertNotNull(tuner);
+    }
+
+    @Test
+    public void testOpenDescrambler() throws Exception {
+        Tuner tuner = new Tuner(mContext, "123", 1, null);
+        Descrambler descrambler = tuner.openDescrambler();
+        assertNotNull(descrambler);
+    }
+}
diff --git a/packages/PrintSpooler/res/values-ja/donottranslate.xml b/packages/PrintSpooler/res/values-ja/donottranslate.xml
index d334ddd..6a0f768 100644
--- a/packages/PrintSpooler/res/values-ja/donottranslate.xml
+++ b/packages/PrintSpooler/res/values-ja/donottranslate.xml
@@ -16,7 +16,7 @@
 
 <resources>
 
-    <string name="mediasize_default">JIS_B5</string>
+    <string name="mediasize_default">ISO_A4</string>
     <string name="mediasize_standard">@string/mediasize_standard_japan</string>
 
 </resources>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cc2c92b..d821050 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -253,7 +253,8 @@
     <!-- Permission required for CTS test - ShortcutManagerUsageTest -->
     <uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/>
 
-     <!-- Permission required for CTS test - UsageStatsTest -->
+    <!-- Permissions required for CTS test - UsageStatsTest -->
+    <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS"/>
     <uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"/>
 
     <!-- Permissions required to test ambient display. -->
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index 95f205a..481c4db 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -14,8 +14,45 @@
   ~ limitations under the License
   -->
 
-<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/bubble_overflow_recycler"
-    android:layout_gravity="center_horizontal"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/bubble_overflow_container"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"/>
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:layout_gravity="center_horizontal">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/bubble_overflow_recycler"
+        android:layout_gravity="center_horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <LinearLayout
+        android:id="@+id/bubble_overflow_empty_state"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <TextView
+            android:id="@+id/bubble_overflow_empty_title"
+            android:text="@string/bubble_overflow_empty_title"
+            android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+            android:textColor="?android:attr/textColorSecondary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"/>
+
+        <TextView
+            android:id="@+id/bubble_overflow_empty_subtitle"
+            android:fontFamily="@*android:string/config_bodyFontFamily"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+            android:textColor="?android:attr/textColorSecondary"
+            android:text="@string/bubble_overflow_empty_subtitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6dd89d8..ef9e705 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1780,6 +1780,12 @@
     <!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary -->
     <string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string>
 
+    <!-- [CHAR LIMIT=NONE] Empty overflow title -->
+    <string name="bubble_overflow_empty_title">No recent bubbles</string>
+
+    <!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
+    <string name="bubble_overflow_empty_subtitle">Recently dismissed bubbles will appear here for easy retrieval.</string>
+
     <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
     <string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 0f7f1be..023b74b 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -19,7 +19,6 @@
 
 import android.annotation.StringRes;
 import android.content.Context;
-import android.view.WindowManager;
 import android.widget.Toast;
 
 public class SysUIToast {
@@ -29,10 +28,7 @@
     }
 
     public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
-        Toast toast = Toast.makeText(context, text, duration);
-        toast.getWindowParams().privateFlags |=
-                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        return toast;
+        return Toast.makeText(context, text, duration);
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 68b05e3..9de1040 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricPrompt;
@@ -33,6 +34,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -126,18 +129,18 @@
         mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
     }
 
-    private void setTextOrHide(TextView view, String string) {
-        if (TextUtils.isEmpty(string)) {
+    private void setTextOrHide(TextView view, CharSequence text) {
+        if (TextUtils.isEmpty(text)) {
             view.setVisibility(View.GONE);
         } else {
-            view.setText(string);
+            view.setText(text);
         }
 
         Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
     }
 
-    private void setText(TextView view, String string) {
-        view.setText(string);
+    private void setText(TextView view, CharSequence text) {
+        view.setText(text);
     }
 
     void setEffectiveUserId(int effectiveUserId) {
@@ -173,11 +176,9 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE));
-        setTextOrHide(mSubtitleView,
-                mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
-        setTextOrHide(mDescriptionView,
-                mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+        setText(mTitleView, getTitle(mBiometricPromptBundle));
+        setTextOrHide(mSubtitleView, getSubtitle(mBiometricPromptBundle));
+        setTextOrHide(mDescriptionView, getDescription(mBiometricPromptBundle));
 
         final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
         final Drawable image;
@@ -279,4 +280,28 @@
             }
         }
     }
+
+    @Nullable
+    private static CharSequence getTitle(@NonNull Bundle bundle) {
+        final CharSequence credentialTitle =
+                bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE);
+        return credentialTitle != null ? credentialTitle
+                : bundle.getCharSequence(BiometricPrompt.KEY_TITLE);
+    }
+
+    @Nullable
+    private static CharSequence getSubtitle(@NonNull Bundle bundle) {
+        final CharSequence credentialSubtitle =
+                bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE);
+        return credentialSubtitle != null ? credentialSubtitle
+                : bundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
+    }
+
+    @Nullable
+    private static CharSequence getDescription(@NonNull Bundle bundle) {
+        final CharSequence credentialDescription =
+                bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION);
+        return credentialDescription != null ? credentialDescription
+                : bundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 45705b7..1e39954 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -226,6 +226,10 @@
         mIconView.update(this);
     }
 
+    void setInflated(boolean inflated) {
+        mInflated = inflated;
+    }
+
     /**
      * Set visibility of bubble in the expanded state.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 05838ab..762e5f2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -749,7 +749,8 @@
     }
 
     void promoteBubbleFromOverflow(Bubble bubble) {
-        mBubbleData.promoteBubbleFromOverflow(bubble);
+        bubble.setInflateSynchronously(mInflateSynchronously);
+        mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 673121f..8a5aad8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -199,16 +199,21 @@
         dispatchPendingChanges();
     }
 
-    public void promoteBubbleFromOverflow(Bubble bubble) {
+    public void promoteBubbleFromOverflow(Bubble bubble, BubbleStackView stack,
+            BubbleIconFactory factory) {
         if (DEBUG_BUBBLE_DATA) {
             Log.d(TAG, "promoteBubbleFromOverflow: " + bubble);
         }
-        mOverflowBubbles.remove(bubble);
-        doAdd(bubble);
-        setSelectedBubbleInternal(bubble);
+
         // Preserve new order for next repack, which sorts by last updated time.
         bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
-        trim();
+        setSelectedBubbleInternal(bubble);
+        mOverflowBubbles.remove(bubble);
+
+        bubble.inflate(
+                b -> notificationEntryUpdated(bubble, /* suppressFlyout */
+                        false, /* showInShade */ true),
+                mContext, stack, factory);
         dispatchPendingChanges();
     }
 
@@ -445,6 +450,10 @@
             mOverflowBubbles.add(0, bubbleToRemove);
             if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
                 // Remove oldest bubble.
+                if (DEBUG_BUBBLE_DATA) {
+                    Log.d(TAG, "Overflow full. Remove bubble: " + mOverflowBubbles.get(
+                            mOverflowBubbles.size() - 1));
+                }
                 mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
             }
         }
@@ -511,7 +520,7 @@
         if (Objects.equals(bubble, mSelectedBubble)) {
             return;
         }
-        if (bubble != null && !mBubbles.contains(bubble)) {
+        if (bubble != null && !mBubbles.contains(bubble) && !mOverflowBubbles.contains(bubble)) {
             Log.e(TAG, "Cannot select bubble which doesn't exist!"
                     + " (" + bubble + ") bubbles=" + mBubbles);
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 0d5261d..fe191f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -294,7 +294,8 @@
         ta.recycle();
 
         mPointerDrawable.setTint(bgColor);
-        if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
+        if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+                mContext.getResources())) {
             mActivityView.setCornerRadius(cornerRadius);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 2d55a1d..f3cfa83 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -26,7 +26,10 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -46,6 +49,7 @@
 public class BubbleOverflowActivity extends Activity {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
 
+    private LinearLayout mEmptyState;
     private BubbleController mBubbleController;
     private BubbleOverflowAdapter mAdapter;
     private RecyclerView mRecyclerView;
@@ -64,6 +68,7 @@
         setBackgroundColor();
 
         mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
+        mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
         mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
         mRecyclerView.setLayoutManager(
                 new GridLayoutManager(getApplicationContext(),
@@ -73,9 +78,9 @@
                 mBubbleController::promoteBubbleFromOverflow);
         mRecyclerView.setAdapter(mAdapter);
 
-        updateData(mBubbleController.getOverflowBubbles());
+        onDataChanged(mBubbleController.getOverflowBubbles());
         mBubbleController.setOverflowCallback(() -> {
-            updateData(mBubbleController.getOverflowBubbles());
+            onDataChanged(mBubbleController.getOverflowBubbles());
         });
     }
 
@@ -87,7 +92,7 @@
         findViewById(android.R.id.content).setBackgroundColor(bgColor);
     }
 
-    void updateData(List<Bubble> bubbles) {
+    void onDataChanged(List<Bubble> bubbles) {
         mOverflowBubbles.clear();
         if (bubbles.size() > mMaxBubbles) {
             mOverflowBubbles.addAll(bubbles.subList(mMaxBubbles, bubbles.size()));
@@ -96,6 +101,12 @@
         }
         mAdapter.notifyDataSetChanged();
 
+        if (mOverflowBubbles.isEmpty()) {
+            mEmptyState.setVisibility(View.VISIBLE);
+        } else {
+            mEmptyState.setVisibility(View.GONE);
+        }
+
         if (DEBUG_OVERFLOW) {
             Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString(
                     mOverflowBubbles, /*selected*/ null));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index bce172b..cff78cf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -528,6 +528,12 @@
         mBubbleContainer.addView(mOverflowBtn, 0,
                 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
 
+        setOverflowBtnTheme();
+        mOverflowBtn.setVisibility(GONE);
+    }
+
+    // TODO(b/149146374) Propagate theme change to bubbles in overflow.
+    private void setOverflowBtnTheme() {
         TypedArray ta = mContext.obtainStyledAttributes(
                 new int[]{android.R.attr.colorBackgroundFloating});
         int bgColor = ta.getColor(0, Color.WHITE /* default */);
@@ -537,8 +543,6 @@
         ColorDrawable bg = new ColorDrawable(bgColor);
         AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
         mOverflowBtn.setImageDrawable(adaptiveIcon);
-
-        mOverflowBtn.setVisibility(GONE);
     }
 
     void showExpandedViewContents(int displayId) {
@@ -568,6 +572,9 @@
      */
     public void onThemeChanged() {
         setUpFlyout();
+        if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+            setOverflowBtnTheme();
+        }
     }
 
     /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -795,6 +802,7 @@
         if (removedIndex >= 0) {
             mBubbleContainer.removeViewAt(removedIndex);
             bubble.cleanupExpandedState();
+            bubble.setInflated(false);
             logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
         } else {
             Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index ab8de26..5c1d332 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -105,8 +105,8 @@
     fun logTileUpdated(tileSpec: String, state: QSTile.State) {
         log(VERBOSE, {
             str1 = tileSpec
-            str2 = state.label.toString()
-            str3 = state.icon.toString()
+            str2 = state.label?.toString()
+            str3 = state.icon?.toString()
             int1 = state.state
             if (state is QSTile.SignalState) {
                 bool1 = true
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 573ea4d..9f64b39 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -352,6 +352,8 @@
             try {
                 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+                        AccessibilityManager.ACCESSIBILITY_BUTTON);
                 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 6d4b13c..91d2de7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -638,9 +638,9 @@
     private static boolean shouldDismissOnClearAll(
             NotificationEntry entry,
             @UserIdInt int userId) {
-        // TODO: (b/149396544) add FLAG_BUBBLE check here + in NoManService
         return userIdMatches(entry, userId)
                 && entry.isClearable()
+                && !hasFlag(entry, Notification.FLAG_BUBBLE)
                 && entry.getDismissState() != DISMISSED;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 4f27c0f..5b4a927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -26,26 +26,23 @@
 import android.content.DialogInterface
 import android.graphics.Color
 import android.graphics.PixelFormat
-import android.graphics.drawable.Drawable
 import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
 import android.util.Log
 import android.view.Gravity
 import android.view.View
 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
 import android.view.Window
-import android.view.WindowInsets.Type
 import android.view.WindowInsets.Type.statusBars
 import android.view.WindowManager
 import android.widget.TextView
 import com.android.internal.annotations.VisibleForTesting
-
 import com.android.systemui.R
-
 import javax.inject.Inject
 import javax.inject.Singleton
 
-const val TAG = "ChannelDialogController"
+private const val TAG = "ChannelDialogController"
 
 /**
  * ChannelEditorDialogController is the controller for the dialog half-shelf
@@ -149,9 +146,9 @@
         val channels = groupList
                 .flatMap { group ->
                     group.channels.asSequence().filterNot { channel ->
-                        channel.isImportanceLockedByOEM
-                                || channel.importance == IMPORTANCE_NONE
-                                || channel.isImportanceLockedByCriticalDeviceFunction
+                        channel.isImportanceLockedByOEM ||
+                                channel.importance == IMPORTANCE_NONE ||
+                                channel.isImportanceLockedByCriticalDeviceFunction
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index e2513da..d744fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -74,11 +74,15 @@
 @Singleton
 public final class NotifBindPipeline {
     private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>();
+    private final NotifBindPipelineLogger mLogger;
     private BindStage mStage;
 
     @Inject
-    NotifBindPipeline(CommonNotifCollection collection) {
+    NotifBindPipeline(
+            CommonNotifCollection collection,
+            NotifBindPipelineLogger logger) {
         collection.addCollectionListener(mCollectionListener);
+        mLogger = logger;
     }
 
     /**
@@ -86,6 +90,8 @@
      */
     public void setStage(
             BindStage stage) {
+        mLogger.logStageSet(stage.getClass().getName());
+
         mStage = stage;
         mStage.setBindRequestListener(this::onBindRequested);
     }
@@ -96,6 +102,8 @@
     public void manageRow(
             @NonNull NotificationEntry entry,
             @NonNull ExpandableNotificationRow row) {
+        mLogger.logManagedRow(entry.getKey());
+
         final BindEntry bindEntry = getBindEntry(entry);
         bindEntry.row = row;
         if (bindEntry.invalidated) {
@@ -130,6 +138,8 @@
      * callbacks when the run finishes. If a run is already in progress, it is restarted.
      */
     private void startPipeline(NotificationEntry entry) {
+        mLogger.logStartPipeline(entry.getKey());
+
         if (mStage == null) {
             throw new IllegalStateException("No stage was ever set on the pipeline");
         }
@@ -147,10 +157,11 @@
 
     private void onPipelineComplete(NotificationEntry entry) {
         final BindEntry bindEntry = getBindEntry(entry);
+        final Set<BindCallback> callbacks = bindEntry.callbacks;
+
+        mLogger.logFinishedPipeline(entry.getKey(), callbacks.size());
 
         bindEntry.invalidated = false;
-
-        final Set<BindCallback> callbacks = bindEntry.callbacks;
         for (BindCallback cb : callbacks) {
             cb.onBindFinished(entry);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
new file mode 100644
index 0000000..2717d7a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class NotifBindPipelineLogger @Inject constructor(
+    @NotificationLog private val buffer: LogBuffer
+) {
+    fun logStageSet(stageName: String) {
+        buffer.log(TAG, INFO, {
+            str1 = stageName
+        }, {
+            "Stage set: $str1"
+        })
+    }
+
+    fun logManagedRow(notifKey: String) {
+        buffer.log(TAG, INFO, {
+            str1 = notifKey
+        }, {
+            "Row set for notif: $str1"
+        })
+    }
+
+    fun logStartPipeline(notifKey: String) {
+        buffer.log(TAG, INFO, {
+            str1 = notifKey
+        }, {
+            "Start pipeline for notif: $str1"
+        })
+    }
+
+    fun logFinishedPipeline(notifKey: String, numCallbacks: Int) {
+        buffer.log(TAG, INFO, {
+            str1 = notifKey
+            int1 = numCallbacks
+        }, {
+            "Finished pipeline for notif $str1 with $int1 callbacks"
+        })
+    }
+}
+
+private const val TAG = "NotifBindPipeline"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 5170d0b..88ed0bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -157,6 +157,15 @@
         return mViewsNeedReinflation;
     }
 
+    @Override
+    public String toString() {
+        return String.format("RowContentBindParams[mContentViews=%x mDirtyContentViews=%x "
+                + "mUseLowPriority=%b mUseChildInGroup=%b mUseIncreasedHeight=%b "
+                + "mUseIncreasedHeadsUpHeight=%b mViewsNeedReinflation=%b]",
+                mContentViews, mDirtyContentViews, mUseLowPriority, mUseChildInGroup,
+                mUseIncreasedHeight, mUseIncreasedHeadsUpHeight, mViewsNeedReinflation);
+    }
+
     /**
      * Content views that should be inflated by default for all notifications.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index f783245..c632f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -38,13 +38,16 @@
 public class RowContentBindStage extends BindStage<RowContentBindParams> {
     private final NotificationRowContentBinder mBinder;
     private final NotifInflationErrorManager mNotifInflationErrorManager;
+    private final RowContentBindStageLogger mLogger;
 
     @Inject
     RowContentBindStage(
             NotificationRowContentBinder binder,
-            NotifInflationErrorManager errorManager) {
+            NotifInflationErrorManager errorManager,
+            RowContentBindStageLogger logger) {
         mBinder = binder;
         mNotifInflationErrorManager = errorManager;
+        mLogger = logger;
     }
 
     @Override
@@ -54,6 +57,8 @@
             @NonNull StageCallback callback) {
         RowContentBindParams params = getStageParams(entry);
 
+        mLogger.logStageParams(entry.getKey(), params.toString());
+
         // Resolve content to bind/unbind.
         @InflationFlag int inflationFlags = params.getContentViews();
         @InflationFlag int invalidatedFlags = params.getDirtyContentViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
new file mode 100644
index 0000000..29cce33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class RowContentBindStageLogger @Inject constructor(
+    @NotificationLog private val buffer: LogBuffer
+) {
+    fun logStageParams(notifKey: String, stageParams: String) {
+        buffer.log(TAG, INFO, {
+            str1 = notifKey
+            str2 = stageParams
+        }, {
+            "Invalidated notif $str1 with params: \n$str2"
+        })
+    }
+}
+
+private const val TAG = "RowContentBindStage"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 3f5215e..fd8c71b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -956,6 +956,8 @@
     private boolean onAccessibilityLongClick(View v) {
         Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+                AccessibilityManager.ACCESSIBILITY_BUTTON);
         v.getContext().startActivityAsUser(intent, UserHandle.CURRENT);
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 70b43bf..e1a20b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -365,8 +365,12 @@
         } catch (RemoteException ex) {
             // system process is dead if we're here.
         }
+
         if (!isBubble) {
             if (parentToCancelFinal != null) {
+                // TODO: (b/145659174) remove - this cancels the parent if the notification clicked
+                // on will auto-cancel and is the only child in the group. This won't be
+                // necessary in the new pipeline due to group pruning in ShadeListBuilder.
                 removeNotification(parentToCancelFinal);
             }
             if (shouldAutoCancel(sbn)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index 408bba4..6408f7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -59,7 +59,7 @@
         MockitoAnnotations.initMocks(this);
         CommonNotifCollection collection = mock(CommonNotifCollection.class);
 
-        mBindPipeline = new NotifBindPipeline(collection);
+        mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class));
         mBindPipeline.setStage(mStage);
 
         ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index fd5512d..7a1bd05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -111,11 +111,13 @@
                 mock(NotifRemoteViewCache.class),
                 mock(NotificationRemoteInputManager.class));
         contentBinder.setInflateSynchronously(true);
-        mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class));
+        mBindStage = new RowContentBindStage(contentBinder,
+                mock(NotifInflationErrorManager.class),
+                mock(RowContentBindStageLogger.class));
 
         CommonNotifCollection collection = mock(CommonNotifCollection.class);
 
-        mBindPipeline = new NotifBindPipeline(collection);
+        mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class));
         mBindPipeline.setStage(mBindStage);
 
         ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index d9fe655..0f2482c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -60,8 +60,10 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mRowContentBindStage = new RowContentBindStage(mBinder,
-                mock(NotifInflationErrorManager.class));
+        mRowContentBindStage = new RowContentBindStage(
+                mBinder,
+                mock(NotifInflationErrorManager.class),
+                mock(RowContentBindStageLogger.class));
         mRowContentBindStage.createStageParams(mEntry);
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index b74be7e..a3b5a3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -17,6 +17,7 @@
 package com.android.server.accessibility.gestures;
 
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
@@ -24,6 +25,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
@@ -31,6 +33,7 @@
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
@@ -132,6 +135,9 @@
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
+                        mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
+        mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
         // Three-finger taps.
         mMultiFingerGestures.add(
@@ -139,6 +145,9 @@
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
+                        mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this));
+        mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
         // Four-finger taps.
         mMultiFingerGestures.add(
@@ -146,6 +155,9 @@
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
+                new MultiFingerMultiTapAndHold(
+                        mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, this));
+        mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
         // Two-finger swipes.
         mMultiFingerGestures.add(
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index 20def61..e5340f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -42,10 +42,10 @@
     // The acceptable distance the pointer can move and still count as a tap.
     private int mTouchSlop;
     // A tap counts when target number of fingers are down and up once.
-    private int mCompletedTapCount;
+    protected int mCompletedTapCount;
     // A flag set to true when target number of fingers have touched down at once before.
     // Used to indicate what next finger action should be. Down when false and lift when true.
-    private boolean mIsTargetFingerCountReached = false;
+    protected boolean mIsTargetFingerCountReached = false;
     // Store initial down points for slop checking and update when next down if is inside slop.
     private PointF[] mBases;
     // The points in bases that already have slop checked when onDown or onPointerDown.
@@ -56,7 +56,11 @@
      * @throws IllegalArgumentException if <code>fingers<code/> is less than 2
      *                                  or <code>taps<code/> is not positive.
      */
-    MultiFingerMultiTap(Context context, int fingers, int taps, int gestureId,
+    MultiFingerMultiTap(
+            Context context,
+            int fingers,
+            int taps,
+            int gestureId,
             GestureMatcher.StateChangeListener listener) {
         super(gestureId, new Handler(context.getMainLooper()), listener);
         Preconditions.checkArgument(fingers >= 2);
@@ -117,8 +121,7 @@
         cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
 
         final PointF nearest = findNearestPoint(rawEvent, mTouchSlop, false);
-        if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR)
-                && null != nearest) {
+        if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && null != nearest) {
             // Increase current tap count when the user have all fingers lifted
             // within the tap timeout since the target number of fingers are down.
             if (mIsTargetFingerCountReached) {
@@ -169,8 +172,7 @@
         } else {
             nearest = findNearestPoint(rawEvent, mDoubleTapSlop, true);
         }
-        if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR)
-                && nearest != null) {
+        if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && nearest != null) {
             // The user have all fingers down within the tap timeout since first finger down,
             // setting the timeout for fingers to be lifted.
             if (currentFingerCount == mTargetFingerCount) {
@@ -227,11 +229,11 @@
     }
 
     /**
-     * Find the nearest location to the given event in the bases.
-     * If no one found, it could be not inside {@code slop}, filtered or empty bases.
-     * When {@code filterMatched} is true, if the location of given event matches one of the points
-     * in {@link #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location
-     * will be added to {@link #mExcludedPointsForDownSlopChecked}.
+     * Find the nearest location to the given event in the bases. If no one found, it could be not
+     * inside {@code slop}, filtered or empty bases. When {@code filterMatched} is true, if the
+     * location of given event matches one of the points in {@link
+     * #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location will be
+     * added to {@link #mExcludedPointsForDownSlopChecked}.
      *
      * @param event to find nearest point in bases.
      * @param slop to check to the given location of the event.
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
new file mode 100644
index 0000000..7824fd9
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -0,0 +1,71 @@
+/*
+ * 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.accessibility.gestures;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+/**
+ * This class matches gestures of the form multi-finger multi-tap and hold. The number of fingers
+ * and taps for each instance is specified in the constructor.
+ */
+class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
+
+    MultiFingerMultiTapAndHold(
+            Context context,
+            int fingers,
+            int taps,
+            int gestureId,
+            GestureMatcher.StateChangeListener listener) {
+        super(context, fingers, taps, gestureId, listener);
+    }
+
+    @Override
+    protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        super.onPointerDown(event, rawEvent, policyFlags);
+        if (mIsTargetFingerCountReached && mCompletedTapCount + 1 == mTargetTapCount) {
+            completeAfterLongPressTimeout(event, rawEvent, policyFlags);
+        }
+    }
+
+    @Override
+    protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mCompletedTapCount + 1 == mTargetFingerCount) {
+            // Calling super.onUp  would complete the multi-tap version of this.
+            cancelGesture(event, rawEvent, policyFlags);
+        } else {
+            super.onUp(event, rawEvent, policyFlags);
+            cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+        }
+    }
+
+    @Override
+    public String getGestureName() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append(mTargetFingerCount).append("-Finger ");
+        if (mTargetTapCount == 1) {
+            builder.append("Single");
+        } else if (mTargetTapCount == 2) {
+            builder.append("Double");
+        } else if (mTargetTapCount == 3) {
+            builder.append("Triple");
+        } else if (mTargetTapCount > 3) {
+            builder.append(mTargetTapCount);
+        }
+        return builder.append(" Tap and hold").toString();
+    }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 4f49fb7..0b3899d 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.appprediction;
 
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppGlobals;
@@ -30,12 +32,17 @@
 import android.content.pm.ServiceInfo;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.provider.DeviceConfig;
 import android.service.appprediction.AppPredictionService;
+import android.service.appprediction.IPredictionService;
 import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.server.LocalServices;
 import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.people.PeopleServiceInternal;
 
 import java.util.function.Consumer;
 
@@ -47,6 +54,8 @@
              implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks {
 
     private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
+    private static final String PREDICT_USING_PEOPLE_SERVICE_PREFIX =
+            "predict_using_people_service_";
 
     @Nullable
     @GuardedBy("mLock")
@@ -104,14 +113,16 @@
     @GuardedBy("mLock")
     public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
             @NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.onCreatePredictionSession(context, sessionId);
-
-            if (!mSessionInfos.containsKey(sessionId)) {
-                mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
-                        this::removeAppPredictionSessionInfo));
-            }
+        if (!mSessionInfos.containsKey(sessionId)) {
+            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
+                    DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+                            PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false),
+                    this::removeAppPredictionSessionInfo));
+        }
+        final boolean serviceExists = resolveService(sessionId, s ->
+                s.onCreatePredictionSession(context, sessionId));
+        if (!serviceExists) {
+            mSessionInfos.remove(sessionId);
         }
     }
 
@@ -121,10 +132,7 @@
     @GuardedBy("mLock")
     public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull AppTargetEvent event) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.notifyAppTargetEvent(sessionId, event);
-        }
+        resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event));
     }
 
     /**
@@ -133,10 +141,8 @@
     @GuardedBy("mLock")
     public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds);
-        }
+        resolveService(sessionId, s ->
+                s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
     }
 
     /**
@@ -145,10 +151,7 @@
     @GuardedBy("mLock")
     public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.sortAppTargets(sessionId, targets, callback);
-        }
+        resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback));
     }
 
     /**
@@ -157,14 +160,11 @@
     @GuardedBy("mLock")
     public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.registerPredictionUpdates(sessionId, callback);
-
-            AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-            if (sessionInfo != null) {
-                sessionInfo.addCallbackLocked(callback);
-            }
+        final boolean serviceExists = resolveService(sessionId, s ->
+                s.registerPredictionUpdates(sessionId, callback));
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (serviceExists && sessionInfo != null) {
+            sessionInfo.addCallbackLocked(callback);
         }
     }
 
@@ -174,14 +174,11 @@
     @GuardedBy("mLock")
     public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.unregisterPredictionUpdates(sessionId, callback);
-
-            AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-            if (sessionInfo != null) {
-                sessionInfo.removeCallbackLocked(callback);
-            }
+        final boolean serviceExists = resolveService(sessionId, s ->
+                s.unregisterPredictionUpdates(sessionId, callback));
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (serviceExists && sessionInfo != null) {
+            sessionInfo.removeCallbackLocked(callback);
         }
     }
 
@@ -190,10 +187,7 @@
      */
     @GuardedBy("mLock")
     public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.requestPredictionUpdate(sessionId);
-        }
+        resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId));
     }
 
     /**
@@ -201,14 +195,11 @@
      */
     @GuardedBy("mLock")
     public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked();
-        if (service != null) {
-            service.onDestroyPredictionSession(sessionId);
-
-            AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-            if (sessionInfo != null) {
-                sessionInfo.destroy();
-            }
+        final boolean serviceExists = resolveService(sessionId, s ->
+                s.onDestroyPredictionSession(sessionId));
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (serviceExists && sessionInfo != null) {
+            sessionInfo.destroy();
         }
     }
 
@@ -312,6 +303,33 @@
 
     @GuardedBy("mLock")
     @Nullable
+    protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId,
+            @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) {
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return false;
+        if (sessionInfo.mUsesPeopleService) {
+            final IPredictionService service =
+                    LocalServices.getService(PeopleServiceInternal.class);
+            if (service != null) {
+                try {
+                    cb.run(service);
+                } catch (RemoteException e) {
+                    // Shouldn't happen.
+                    Slog.w(TAG, "Failed to invoke service:" + service, e);
+                }
+            }
+            return service != null;
+        } else {
+            final RemoteAppPredictionService service = getRemoteServiceLocked();
+            if (service != null) {
+                service.scheduleOnResolvedService(cb);
+            }
+            return service != null;
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
     private RemoteAppPredictionService getRemoteServiceLocked() {
         if (mRemoteService == null) {
             final String serviceName = getComponentNameLocked();
@@ -334,8 +352,12 @@
     private static final class AppPredictionSessionInfo {
         private static final boolean DEBUG = false;  // Do not submit with true
 
+        @NonNull
         private final AppPredictionSessionId mSessionId;
+        @NonNull
         private final AppPredictionContext mPredictionContext;
+        private final boolean mUsesPeopleService;
+        @NonNull
         private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
 
         private final RemoteCallbackList<IPredictionCallback> mCallbacks =
@@ -352,13 +374,17 @@
                     }
                 };
 
-        AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext,
-                Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+        AppPredictionSessionInfo(
+                @NonNull final AppPredictionSessionId id,
+                @NonNull final AppPredictionContext predictionContext,
+                final boolean usesPeopleService,
+                @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
             if (DEBUG) {
                 Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
             }
             mSessionId = id;
             mPredictionContext = predictionContext;
+            mUsesPeopleService = usesPeopleService;
             mRemoveSessionInfoAction = removeSessionInfoAction;
         }
 
diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
index 04e0e7f..ceb1caf 100644
--- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
+++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
@@ -16,13 +16,8 @@
 package com.android.server.appprediction;
 
 import android.annotation.NonNull;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionSessionId;
-import android.app.prediction.AppTargetEvent;
-import android.app.prediction.IPredictionCallback;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.ParceledListSlice;
 import android.os.IBinder;
 import android.service.appprediction.IPredictionService;
 import android.text.format.DateUtils;
@@ -71,70 +66,6 @@
     }
 
     /**
-     * Notifies the service of a new prediction session.
-     */
-    public void onCreatePredictionSession(@NonNull AppPredictionContext context,
-            @NonNull AppPredictionSessionId sessionId) {
-        scheduleAsyncRequest((s) -> s.onCreatePredictionSession(context, sessionId));
-    }
-
-    /**
-     * Records an app target event to the service.
-     */
-    public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
-            @NonNull AppTargetEvent event) {
-        scheduleAsyncRequest((s) -> s.notifyAppTargetEvent(sessionId, event));
-    }
-
-    /**
-     * Records when a launch location is shown.
-     */
-    public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId,
-            @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        scheduleAsyncRequest((s)
-                -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
-    }
-
-    /**
-     * Requests the service to sort a list of apps or shortcuts.
-     */
-    public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
-            @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        scheduleAsyncRequest((s) -> s.sortAppTargets(sessionId, targets, callback));
-    }
-
-
-    /**
-     * Registers a callback for continuous updates of predicted apps or shortcuts.
-     */
-    public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
-            @NonNull IPredictionCallback callback) {
-        scheduleAsyncRequest((s) -> s.registerPredictionUpdates(sessionId, callback));
-    }
-
-    /**
-     * Unregisters a callback for continuous updates of predicted apps or shortcuts.
-     */
-    public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
-            @NonNull IPredictionCallback callback) {
-        scheduleAsyncRequest((s) -> s.unregisterPredictionUpdates(sessionId, callback));
-    }
-
-    /**
-     * Requests a new set of predicted apps or shortcuts.
-     */
-    public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
-        scheduleAsyncRequest((s) -> s.requestPredictionUpdate(sessionId));
-    }
-
-    /**
-     * Notifies the service of the end of an existing prediction session.
-     */
-    public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
-        scheduleAsyncRequest((s) -> s.onDestroyPredictionSession(sessionId));
-    }
-
-    /**
      * Schedules a request to bind to the remote service.
      */
     public void reconnect() {
@@ -142,6 +73,13 @@
     }
 
     /**
+     * Schedule async request on remote service.
+     */
+    public void scheduleOnResolvedService(@NonNull AsyncRequest<IPredictionService> request) {
+        scheduleAsyncRequest(request);
+    }
+
+    /**
      * Failure callback
      */
     public interface RemoteAppPredictionServiceCallbacks
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 31ea5fa..9a33fc9 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -998,6 +998,11 @@
 
                     sendErrorSignal(mClientAdapterReference, serviceAdapterReference,
                             ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
+                } finally {
+                    synchronized (parentService.mLock) {
+                        parentService.mPackagesWithShareRequests
+                                .remove(mDataShareRequest.getPackageName());
+                    }
                 }
             });
 
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 442c9e5..f688759 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -216,17 +216,11 @@
     /**
      * Returns the events for the user in the given time period.
      *
-     * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
-     *     result.
-     * @param hideShortcutInvocationEvents whether the {@link UsageEvents.Event#SHORTCUT_INVOCATION}
-     *     events need to be excluded from the result.
-     * @param hideLocusIdEvents whether the {@link UsageEvents.Event#LOCUS_ID_SET}
-     *     events need to be excluded from the result.
-     *
+     * @param flags defines the visibility of certain usage events - see flags defined in
+     * {@link UsageEvents}.
      */
     public abstract UsageEvents queryEventsForUser(@UserIdInt int userId, long beginTime,
-            long endTime, boolean obfuscateInstantApps, boolean hideShortcutInvocationEvents,
-            boolean hideLocusIdEvents);
+            long endTime, int flags);
 
     /**
      * Used to persist the last time a job was run for this app, in order to make decisions later
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index c987620..9540f43 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1556,16 +1556,16 @@
         }
 
         Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
-        switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
-            case AppOpsManager.MODE_DEFAULT:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
-                break;
-            case AppOpsManager.MODE_ALLOWED:
-                return;
-            default:
-                throw new SecurityException("Request to ignore AppOps for non-legacy API");
+
+        // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
+        // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
+        // permission or is the System Server.
+        if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
+                TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+            return;
         }
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
     }
 
     private void createOrUpdateTransform(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 75e310d..b5b22f1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1300,13 +1300,6 @@
                     vol.state = newState;
                     onVolumeStateChangedLocked(vol, oldState, newState);
                 }
-                try {
-                    if (vol.type == VolumeInfo.TYPE_PRIVATE && state == VolumeInfo.STATE_MOUNTED) {
-                        mInstaller.onPrivateVolumeMounted(vol.getFsUuid());
-                    }
-                } catch (Installer.InstallerException e) {
-                    Slog.i(TAG, "Failed when private volume mounted " + vol, e);
-                }
             }
         }
 
@@ -3110,6 +3103,15 @@
 
         try {
             mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
+            // After preparing user storage, we should check if we should mount data mirror again,
+            // and we do it for user 0 only as we only need to do once for all users.
+            if (volumeUuid != null) {
+                final StorageManager storage = mContext.getSystemService(StorageManager.class);
+                VolumeInfo info = storage.findVolumeByUuid(volumeUuid);
+                if (info != null && userId == 0 && info.type == VolumeInfo.TYPE_PRIVATE) {
+                    mInstaller.tryMountDataMirror(volumeUuid);
+                }
+            }
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 0e5a6bb..f85fc28 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -61,6 +61,7 @@
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DataFailCause;
 import android.telephony.DisconnectCause;
+import android.telephony.DisplayInfo;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
@@ -205,6 +206,8 @@
 
     private boolean[] mUserMobileDataState;
 
+    private DisplayInfo[] mDisplayInfos;
+
     private SignalStrength[] mSignalStrength;
 
     private boolean[] mMessageWaiting;
@@ -284,7 +287,8 @@
     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
+                        | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
 
     static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE
@@ -443,6 +447,7 @@
         mCallAttributes = copyOf(mCallAttributes, mNumPhones);
         mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
         mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
+        mDisplayInfos = copyOf(mDisplayInfos, mNumPhones);
 
         // ds -> ss switch.
         if (mNumPhones < oldNumPhones) {
@@ -482,6 +487,7 @@
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
             mBarringInfo.add(i, new BarringInfo());
+            mDisplayInfos[i] = null;
         }
     }
 
@@ -540,6 +546,7 @@
         mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
         mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
         mBarringInfo = new ArrayList<>();
+        mDisplayInfos = new DisplayInfo[numPhones];
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -568,6 +575,7 @@
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
             mBarringInfo.add(i, new BarringInfo());
+            mDisplayInfos[i] = null;
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -978,6 +986,15 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+                        try {
+                            if (mDisplayInfos[phoneId] != null) {
+                                r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                     if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
                         try {
                             r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -1501,6 +1518,45 @@
         }
     }
 
+    /**
+     * Notify display network info changed.
+     *
+     * @param phoneId Phone id
+     * @param subId Subscription id
+     * @param displayInfo Display network info
+     *
+     * @see PhoneStateListener#onDisplayInfoChanged(DisplayInfo)
+     */
+    public void notifyDisplayInfoChanged(int phoneId, int subId,
+                                         @NonNull DisplayInfo displayInfo) {
+        if (!checkNotifyPermission("notifyDisplayInfoChanged()")) {
+            return;
+        }
+        if (VDBG) {
+            log("notifyDisplayInfoChanged: PhoneId=" + phoneId
+                    + " subId=" + subId + " displayInfo=" + displayInfo);
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                if (mDisplayInfos[phoneId] != null) {
+                    mDisplayInfos[phoneId] = displayInfo;
+                    for (Record r : mRecords) {
+                        if (r.matchPhoneStateListenerEvent(
+                                PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
+                                && idMatch(r.subId, subId, phoneId)) {
+                            try {
+                                r.callback.onDisplayInfoChanged(displayInfo);
+                            } catch (RemoteException ex) {
+                                mRemoveList.add(r.binder);
+                            }
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     public void notifyCallForwardingChanged(boolean cfi) {
         notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi);
     }
@@ -2730,6 +2786,20 @@
             }
         }
 
+        if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+            try {
+                if (VDBG) {
+                    log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
+                            + phoneId + " dpi=" + mDisplayInfos[phoneId]);
+                }
+                if (mDisplayInfos[phoneId] != null) {
+                    r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]);
+                }
+            } catch (RemoteException ex) {
+                mRemoveList.add(r.binder);
+            }
+        }
+
         if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
             try {
                 if (VDBG) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index abe0dd5..ffa7d92 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -59,6 +59,8 @@
 import android.app.ApplicationExitInfo.SubReason;
 import android.app.IApplicationThread;
 import android.app.IUidObserver;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -119,6 +121,7 @@
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowManagerService;
 
+import dalvik.annotation.compat.VersionCodes;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -327,6 +330,15 @@
      */
     private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds;
 
+    /**
+     * Native heap allocations will now have a non-zero tag in the most significant byte.
+     * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+     * Pointers</a>
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+    private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
     ActivityManagerService mService = null;
 
     // To kill process groups asynchronously
@@ -1768,6 +1780,13 @@
                 runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
             }
 
+            // Enable heap pointer tagging, unless disabled by the app manifest, target sdk level,
+            // or the compat feature.
+            if (app.info.allowsNativeHeapPointerTagging()
+                    && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+                runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            }
+
             String invokeWith = null;
             if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                 // Debuggable apps may include a wrapper script with their library directory.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6911e64..3ffe1be 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -410,9 +410,9 @@
                     Slog.e(TAG, "Bad app ops settings", e);
                 }
                 TOP_STATE_SETTLE_TIME = mParser.getDurationMillis(
-                        KEY_TOP_STATE_SETTLE_TIME, 30 * 1000L);
+                        KEY_TOP_STATE_SETTLE_TIME, 5 * 1000L);
                 FG_SERVICE_STATE_SETTLE_TIME = mParser.getDurationMillis(
-                        KEY_FG_SERVICE_STATE_SETTLE_TIME, 10 * 1000L);
+                        KEY_FG_SERVICE_STATE_SETTLE_TIME, 5 * 1000L);
                 BG_STATE_SETTLE_TIME = mParser.getDurationMillis(
                         KEY_BG_STATE_SETTLE_TIME, 1 * 1000L);
             }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 97a8b87..67d7530 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -128,6 +128,7 @@
 import android.util.MathUtils;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -247,6 +248,7 @@
     // AudioHandler messages
     private static final int MSG_SET_DEVICE_VOLUME = 0;
     private static final int MSG_PERSIST_VOLUME = 1;
+    private static final int MSG_PERSIST_VOLUME_GROUP = 2;
     private static final int MSG_PERSIST_RINGER_MODE = 3;
     private static final int MSG_AUDIO_SERVER_DIED = 4;
     private static final int MSG_PLAY_SOUND_EFFECT = 5;
@@ -780,6 +782,10 @@
         mSettingsObserver = new SettingsObserver();
         createStreamStates();
 
+        // must be called after createStreamStates() as it uses MUSIC volume as default if no
+        // persistent data
+        initVolumeGroupStates();
+
         // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
         // relies on audio policy having correct ranges for volume indexes.
         mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
@@ -1018,6 +1024,9 @@
             streamState.applyAllVolumes();
         }
 
+        // Restore audio volume groups
+        restoreVolumeGroups();
+
         // Restore mono mode
         updateMasterMono(mContentResolver);
 
@@ -2288,20 +2297,20 @@
                                             String callingPackage) {
         enforceModifyAudioRoutingPermission();
         Objects.requireNonNull(attr, "attr must not be null");
-        // @todo not hold the caller context, post message
-        int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
-        final int device = getDeviceForStream(stream);
-
-        int oldIndex = AudioSystem.getVolumeIndexForAttributes(attr, device);
-
-        AudioSystem.setVolumeIndexForAttributes(attr, index, device);
-
         final int volumeGroup = getVolumeGroupIdForAttributes(attr);
-        final AudioVolumeGroup avg = getAudioVolumeGroupById(volumeGroup);
-        if (avg == null) {
+        if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
+            Log.e(TAG, ": no volume group found for attributes " + attr.toString());
             return;
         }
-        for (final int groupedStream : avg.getLegacyStreamTypes()) {
+        final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+
+        sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
+                index/*val1*/, flags/*val2*/, callingPackage));
+
+        vgs.setVolumeIndex(index, flags);
+
+        // For legacy reason, propagate to all streams associated to this volume group
+        for (final int groupedStream : vgs.getLegacyStreamTypes()) {
             setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
                             Binder.getCallingUid());
         }
@@ -2323,10 +2332,12 @@
     public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         enforceModifyAudioRoutingPermission();
         Objects.requireNonNull(attr, "attr must not be null");
-        int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
-        final int device = getDeviceForStream(stream);
-
-        return AudioSystem.getVolumeIndexForAttributes(attr, device);
+        final int volumeGroup = getVolumeGroupIdForAttributes(attr);
+        if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
+            throw new IllegalArgumentException("No volume group for attributes " + attr);
+        }
+        final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+        return vgs.getVolumeIndex();
     }
 
     /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
@@ -3754,6 +3765,8 @@
                 enforceSafeMediaVolume(TAG);
             }
         }
+
+        readVolumeGroupsSettings();
     }
 
     /** @see AudioManager#setSpeakerphoneOn(boolean) */
@@ -4654,6 +4667,310 @@
     ///////////////////////////////////////////////////////////////////////////
     // Inner classes
     ///////////////////////////////////////////////////////////////////////////
+    /**
+     * Key is the AudioManager VolumeGroupId
+     * Value is the VolumeGroupState
+     */
+    private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>();
+
+    private void initVolumeGroupStates() {
+        for (final AudioVolumeGroup avg : getAudioVolumeGroups()) {
+            try {
+                // if no valid attributes, this volume group is not controllable, throw exception
+                ensureValidAttributes(avg);
+            } catch (IllegalArgumentException e) {
+                // Volume Groups without attributes are not controllable through set/get volume
+                // using attributes. Do not append them.
+                Log.d(TAG, "volume group " + avg.name() + " for internal policy needs");
+                continue;
+            }
+            sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
+        }
+        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+            vgs.applyAllVolumes();
+        }
+    }
+
+    private void ensureValidAttributes(AudioVolumeGroup avg) {
+        boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
+                .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+        if (!hasAtLeastOneValidAudioAttributes) {
+            throw new IllegalArgumentException("Volume Group " + avg.name()
+                    + " has no valid audio attributes");
+        }
+    }
+
+    private void readVolumeGroupsSettings() {
+        Log.v(TAG, "readVolumeGroupsSettings");
+        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+            vgs.readSettings();
+            vgs.applyAllVolumes();
+        }
+    }
+
+    // Called upon crash of AudioServer
+    private void restoreVolumeGroups() {
+        Log.v(TAG, "restoreVolumeGroups");
+        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+            vgs.applyAllVolumes();
+        }
+    }
+
+    private void dumpVolumeGroups(PrintWriter pw) {
+        pw.println("\nVolume Groups (device: index)");
+        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+            vgs.dump(pw);
+            pw.println("");
+        }
+    }
+
+    // NOTE: Locking order for synchronized objects related to volume management:
+    //  1     mSettingsLock
+    //  2       VolumeGroupState.class
+    private class VolumeGroupState {
+        private final AudioVolumeGroup mAudioVolumeGroup;
+        private final SparseIntArray mIndexMap = new SparseIntArray(8);
+        private int mIndexMin;
+        private int mIndexMax;
+        private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
+        private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
+        private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+
+        // No API in AudioSystem to get a device from strategy or from attributes.
+        // Need a valid public stream type to use current API getDeviceForStream
+        private int getDeviceForVolume() {
+            return getDeviceForStream(mPublicStreamType);
+        }
+
+        private VolumeGroupState(AudioVolumeGroup avg) {
+            mAudioVolumeGroup = avg;
+            Log.v(TAG, "VolumeGroupState for " + avg.toString());
+            for (final AudioAttributes aa : avg.getAudioAttributes()) {
+                if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+                    mAudioAttributes = aa;
+                    break;
+                }
+            }
+            final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
+            if (streamTypes.length != 0) {
+                // Uses already initialized MIN / MAX if a stream type is attached to group
+                mLegacyStreamType = streamTypes[0];
+                for (final int streamType : streamTypes) {
+                    if (streamType != AudioSystem.STREAM_DEFAULT
+                            && streamType < AudioSystem.getNumStreamTypes()) {
+                        mPublicStreamType = streamType;
+                        break;
+                    }
+                }
+                mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType];
+                mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType];
+            } else if (!avg.getAudioAttributes().isEmpty()) {
+                mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes);
+                mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes);
+            } else {
+                Log.e(TAG, "volume group: " + mAudioVolumeGroup.name()
+                        + " has neither valid attributes nor valid stream types assigned");
+                return;
+            }
+            // Load volume indexes from data base
+            readSettings();
+        }
+
+        public @NonNull int[] getLegacyStreamTypes() {
+            return mAudioVolumeGroup.getLegacyStreamTypes();
+        }
+
+        public String name() {
+            return mAudioVolumeGroup.name();
+        }
+
+        public int getVolumeIndex() {
+            return getIndex(getDeviceForVolume());
+        }
+
+        public void setVolumeIndex(int index, int flags) {
+            if (mUseFixedVolume) {
+                return;
+            }
+            setVolumeIndex(index, getDeviceForVolume(), flags);
+        }
+
+        private void setVolumeIndex(int index, int device, int flags) {
+            // Set the volume index
+            setVolumeIndexInt(index, device, flags);
+
+            // Update local cache
+            mIndexMap.put(device, index);
+
+            // update data base - post a persist volume group msg
+            sendMsg(mAudioHandler,
+                    MSG_PERSIST_VOLUME_GROUP,
+                    SENDMSG_QUEUE,
+                    device,
+                    0,
+                    this,
+                    PERSIST_DELAY);
+        }
+
+        private void setVolumeIndexInt(int index, int device, int flags) {
+            // Set the volume index
+            AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+        }
+
+        public int getIndex(int device) {
+            synchronized (VolumeGroupState.class) {
+                int index = mIndexMap.get(device, -1);
+                // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+                return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
+            }
+        }
+
+        public boolean hasIndexForDevice(int device) {
+            synchronized (VolumeGroupState.class) {
+                return (mIndexMap.get(device, -1) != -1);
+            }
+        }
+
+        public int getMaxIndex() {
+            return mIndexMax;
+        }
+
+        public int getMinIndex() {
+            return mIndexMin;
+        }
+
+        public void applyAllVolumes() {
+            synchronized (VolumeGroupState.class) {
+                if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) {
+                    // No-op to avoid regression with stream based volume management
+                    return;
+                }
+                // apply device specific volumes first
+                int index;
+                for (int i = 0; i < mIndexMap.size(); i++) {
+                    final int device = mIndexMap.keyAt(i);
+                    if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
+                        index = mIndexMap.valueAt(i);
+                        Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
+                                + mAudioVolumeGroup.name() + " and device "
+                                + AudioSystem.getOutputDeviceName(device));
+                        setVolumeIndexInt(index, device, 0 /*flags*/);
+                    }
+                }
+                // apply default volume last: by convention , default device volume will be used
+                index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
+                Log.v(TAG, "applyAllVolumes: restore default device index " + index + " for group "
+                        + mAudioVolumeGroup.name());
+                setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
+            }
+        }
+
+        private void persistVolumeGroup(int device) {
+            if (mUseFixedVolume) {
+                return;
+            }
+            Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
+                    + mAudioVolumeGroup.name() + " and device "
+                    + AudioSystem.getOutputDeviceName(device));
+            boolean success = Settings.System.putIntForUser(mContentResolver,
+                    getSettingNameForDevice(device),
+                    getIndex(device),
+                    UserHandle.USER_CURRENT);
+            if (!success) {
+                Log.e(TAG, "persistVolumeGroup failed for group " +  mAudioVolumeGroup.name());
+            }
+        }
+
+        public void readSettings() {
+            synchronized (VolumeGroupState.class) {
+                // First clear previously loaded (previous user?) settings
+                mIndexMap.clear();
+                // force maximum volume on all streams if fixed volume property is set
+                if (mUseFixedVolume) {
+                    mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+                    return;
+                }
+                for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
+                    // retrieve current volume for device
+                    // if no volume stored for current volume group and device, use default volume
+                    // if default device, continue otherwise
+                    int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT)
+                            ? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1;
+                    int index;
+                    String name = getSettingNameForDevice(device);
+                    index = Settings.System.getIntForUser(
+                            mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+                    if (index == -1) {
+                        Log.e(TAG, "readSettings: No index stored for group "
+                                + mAudioVolumeGroup.name() + ", device " + name);
+                        continue;
+                    }
+                    Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+                             + " for group " + mAudioVolumeGroup.name() + ", device: " + name);
+                    mIndexMap.put(device, getValidIndex(index));
+                }
+            }
+        }
+
+        private int getValidIndex(int index) {
+            if (index < mIndexMin) {
+                return mIndexMin;
+            } else if (mUseFixedVolume || index > mIndexMax) {
+                return mIndexMax;
+            }
+            return index;
+        }
+
+        public @NonNull String getSettingNameForDevice(int device) {
+            final String suffix = AudioSystem.getOutputDeviceName(device);
+            if (suffix.isEmpty()) {
+                return mAudioVolumeGroup.name();
+            }
+            return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device);
+        }
+
+        private void dump(PrintWriter pw) {
+            pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
+            pw.print("   Min: ");
+            pw.println(mIndexMin);
+            pw.print("   Max: ");
+            pw.println(mIndexMax);
+            pw.print("   Current: ");
+            for (int i = 0; i < mIndexMap.size(); i++) {
+                if (i > 0) {
+                    pw.print(", ");
+                }
+                final int device = mIndexMap.keyAt(i);
+                pw.print(Integer.toHexString(device));
+                final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
+                        : AudioSystem.getOutputDeviceName(device);
+                if (!deviceName.isEmpty()) {
+                    pw.print(" (");
+                    pw.print(deviceName);
+                    pw.print(")");
+                }
+                pw.print(": ");
+                pw.print(mIndexMap.valueAt(i));
+            }
+            pw.println();
+            pw.print("   Devices: ");
+            int n = 0;
+            final int devices = getDeviceForVolume();
+            for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
+                if ((devices & device) == device) {
+                    if (n++ > 0) {
+                        pw.print(", ");
+                    }
+                    pw.print(AudioSystem.getOutputDeviceName(device));
+                }
+            }
+        }
+    }
+
 
     // NOTE: Locking order for synchronized objects related to volume or ringer mode management:
     //  1 mScoclient OR mSafeMediaVolumeState
@@ -5298,6 +5615,11 @@
                     persistVolume((VolumeStreamState) msg.obj, msg.arg1);
                     break;
 
+                case MSG_PERSIST_VOLUME_GROUP:
+                    final VolumeGroupState vgs = (VolumeGroupState) msg.obj;
+                    vgs.persistVolumeGroup(msg.arg1);
+                    break;
+
                 case MSG_PERSIST_RINGER_MODE:
                     // note that the value persisted is the current ringer mode, not the
                     // value of ringer mode as of the time the request was made to persist
@@ -6395,6 +6717,7 @@
         }
         mMediaFocusControl.dump(pw);
         dumpStreamStates(pw);
+        dumpVolumeGroups(pw);
         dumpRingerMode(pw);
         pw.println("\nAudio routes:");
         pw.print("  mMainType=0x"); pw.println(Integer.toHexString(
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index fcd8701..add620e 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -16,6 +16,7 @@
 
 package com.android.server.audio;
 
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 
@@ -97,12 +98,15 @@
         static final int VOL_ADJUST_VOL_UID = 5;
         static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
         static final int VOL_MODE_CHANGE_HEARING_AID = 7;
+        static final int VOL_SET_GROUP_VOL = 8;
 
         final int mOp;
         final int mStream;
         final int mVal1;
         final int mVal2;
         final String mCaller;
+        final String mGroupName;
+        final AudioAttributes mAudioAttributes;
 
         /** used for VOL_ADJUST_VOL_UID,
          *           VOL_ADJUST_SUGG_VOL,
@@ -114,6 +118,8 @@
             mVal1 = val1;
             mVal2 = val2;
             mCaller = caller;
+            mGroupName = null;
+            mAudioAttributes = null;
         }
 
         /** used for VOL_SET_HEARING_AID_VOL*/
@@ -124,6 +130,8 @@
             // unused
             mStream = -1;
             mCaller = null;
+            mGroupName = null;
+            mAudioAttributes = null;
         }
 
         /** used for VOL_SET_AVRCP_VOL */
@@ -134,6 +142,8 @@
             mVal2 = 0;
             mStream = -1;
             mCaller = null;
+            mGroupName = null;
+            mAudioAttributes = null;
         }
 
         /** used for VOL_VOICE_ACTIVITY_HEARING_AID */
@@ -144,6 +154,8 @@
             mVal2 = voiceActive ? 1 : 0;
             // unused
             mCaller = null;
+            mGroupName = null;
+            mAudioAttributes = null;
         }
 
         /** used for VOL_MODE_CHANGE_HEARING_AID */
@@ -154,6 +166,19 @@
             mVal2 = mode;
             // unused
             mCaller = null;
+            mGroupName = null;
+            mAudioAttributes = null;
+        }
+
+        /** used for VOL_SET_GROUP_VOL */
+        VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) {
+            mOp = op;
+            mStream = -1;
+            mVal1 = index;
+            mVal2 = flags;
+            mCaller = caller;
+            mGroupName = group;
+            mAudioAttributes = aa;
         }
 
         @Override
@@ -208,6 +233,14 @@
                             .append(") causes setting HEARING_AID volume to idx:").append(mVal1)
                             .append(" stream:").append(AudioSystem.streamToString(mStream))
                             .toString();
+                case VOL_SET_GROUP_VOL:
+                    return new StringBuilder("setVolumeIndexForAttributes(attr:")
+                            .append(mAudioAttributes.toString())
+                            .append(" group: ").append(mGroupName)
+                            .append(" index:").append(mVal1)
+                            .append(" flags:0x").append(Integer.toHexString(mVal2))
+                            .append(") from ").append(mCaller)
+                            .toString();
                 default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
             }
         }
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index c9c2c96..204f072 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -153,7 +153,12 @@
 
             // Only allow internal clients to enable non-public options.
             if (bundle.getBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS)
-                    || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+                    || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)
+                    || bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE) != null
+                    || bundle.getCharSequence(
+                            BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE) != null
+                    || bundle.getCharSequence(
+                            BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION) != null) {
                 checkInternalPermission();
             }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index cb88c4e..1a68f1b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,8 +48,12 @@
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
 import android.net.INetworkManagementEventObserver;
+import android.net.Ikev2VpnProfile;
 import android.net.IpPrefix;
 import android.net.IpSecManager;
+import android.net.IpSecManager.IpSecTunnelInterface;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LocalSocket;
@@ -65,6 +69,12 @@
 import android.net.UidRange;
 import android.net.VpnManager;
 import android.net.VpnService;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionParams;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
@@ -113,6 +123,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -122,6 +133,9 @@
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -176,14 +190,14 @@
 
     private final Context mContext;
     private final NetworkInfo mNetworkInfo;
-    private String mPackage;
+    @VisibleForTesting protected String mPackage;
     private int mOwnerUID;
     private boolean mIsPackageTargetingAtLeastQ;
     private String mInterface;
     private Connection mConnection;
 
     /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
-    private VpnRunner mVpnRunner;
+    @VisibleForTesting protected VpnRunner mVpnRunner;
 
     private PendingIntent mStatusIntent;
     private volatile boolean mEnableTeardown = true;
@@ -196,6 +210,7 @@
     @VisibleForTesting
     protected final NetworkCapabilities mNetworkCapabilities;
     private final SystemServices mSystemServices;
+    private final Ikev2SessionCreator mIkev2SessionCreator;
 
     /**
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -238,17 +253,20 @@
 
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
             @UserIdInt int userHandle) {
-        this(looper, context, netService, userHandle, new SystemServices(context));
+        this(looper, context, netService, userHandle,
+                new SystemServices(context), new Ikev2SessionCreator());
     }
 
     @VisibleForTesting
     protected Vpn(Looper looper, Context context, INetworkManagementService netService,
-            int userHandle, SystemServices systemServices) {
+            int userHandle, SystemServices systemServices,
+            Ikev2SessionCreator ikev2SessionCreator) {
         mContext = context;
         mNetd = netService;
         mUserHandle = userHandle;
         mLooper = looper;
         mSystemServices = systemServices;
+        mIkev2SessionCreator = ikev2SessionCreator;
 
         mPackage = VpnConfig.LEGACY_VPN;
         mOwnerUID = getAppUid(mPackage, mUserHandle);
@@ -749,8 +767,9 @@
 
     private boolean isCurrentPreparedPackage(String packageName) {
         // We can't just check that packageName matches mPackage, because if the app was uninstalled
-        // and reinstalled it will no longer be prepared. Instead check the UID.
-        return getAppUid(packageName, mUserHandle) == mOwnerUID;
+        // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
+        // calling package may not be the same as the prepared package. Check both UID and package.
+        return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName);
     }
 
     /** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -979,7 +998,11 @@
         }
         lp.setDomains(buffer.toString().trim());
 
-        // TODO: Stop setting the MTU in jniCreate and set it here.
+        if (mConfig.mtu > 0) {
+            lp.setMtu(mConfig.mtu);
+        }
+
+        // TODO: Stop setting the MTU in jniCreate
 
         return lp;
     }
@@ -2004,30 +2027,369 @@
         protected abstract void exit();
     }
 
-    private class IkeV2VpnRunner extends VpnRunner {
-        private static final String TAG = "IkeV2VpnRunner";
+    interface IkeV2VpnRunnerCallback {
+        void onDefaultNetworkChanged(@NonNull Network network);
 
-        private final IpSecManager mIpSecManager;
-        private final VpnProfile mProfile;
+        void onChildOpened(
+                @NonNull Network network, @NonNull ChildSessionConfiguration childConfig);
 
-        IkeV2VpnRunner(VpnProfile profile) {
+        void onChildTransformCreated(
+                @NonNull Network network, @NonNull IpSecTransform transform, int direction);
+
+        void onSessionLost(@NonNull Network network);
+    }
+
+    /**
+     * Internal class managing IKEv2/IPsec VPN connectivity
+     *
+     * <p>The IKEv2 VPN will listen to, and run based on the lifecycle of Android's default Network.
+     * As a new default is selected, old IKE sessions will be torn down, and a new one will be
+     * started.
+     *
+     * <p>This class uses locking minimally - the Vpn instance lock is only ever held when fields of
+     * the outer class are modified. As such, care must be taken to ensure that no calls are added
+     * that might modify the outer class' state without acquiring a lock.
+     *
+     * <p>The overall structure of the Ikev2VpnRunner is as follows:
+     *
+     * <ol>
+     *   <li>Upon startup, a NetworkRequest is registered with ConnectivityManager. This is called
+     *       any time a new default network is selected
+     *   <li>When a new default is connected, an IKE session is started on that Network. If there
+     *       were any existing IKE sessions on other Networks, they are torn down before starting
+     *       the new IKE session
+     *   <li>Upon establishment, the onChildTransformCreated() callback is called twice, one for
+     *       each direction, and finally onChildOpened() is called
+     *   <li>Upon the onChildOpened() call, the VPN is fully set up.
+     *   <li>Subsequent Network changes result in new onDefaultNetworkChanged() callbacks. See (2).
+     * </ol>
+     */
+    class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback {
+        @NonNull private static final String TAG = "IkeV2VpnRunner";
+
+        @NonNull private final IpSecManager mIpSecManager;
+        @NonNull private final Ikev2VpnProfile mProfile;
+        @NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
+
+        /**
+         * Executor upon which ALL callbacks must be run.
+         *
+         * <p>This executor MUST be a single threaded executor, in order to ensure the consistency
+         * of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by
+         * virtue of everything being serialized on this executor.
+         */
+        @NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+        /** Signal to ensure shutdown is honored even if a new Network is connected. */
+        private boolean mIsRunning = true;
+
+        @Nullable private UdpEncapsulationSocket mEncapSocket;
+        @Nullable private IpSecTunnelInterface mTunnelIface;
+        @Nullable private IkeSession mSession;
+        @Nullable private Network mActiveNetwork;
+
+        IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
             super(TAG);
             mProfile = profile;
-
-            // TODO: move this to startVpnRunnerPrivileged()
-            mConfig = new VpnConfig();
-            mIpSecManager = mContext.getSystemService(IpSecManager.class);
+            mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
+            mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this);
         }
 
         @Override
         public void run() {
-            // TODO: Build IKE config, start IKE session
+            // Explicitly use only the network that ConnectivityService thinks is the "best." In
+            // other words, only ever use the currently selected default network. This does mean
+            // that in both onLost() and onConnected(), any old sessions MUST be torn down. This
+            // does NOT include VPNs.
+            final ConnectivityManager cm = ConnectivityManager.from(mContext);
+            cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
+        }
+
+        private boolean isActiveNetwork(@Nullable Network network) {
+            return Objects.equals(mActiveNetwork, network) && mIsRunning;
+        }
+
+        /**
+         * Called when an IKE Child session has been opened, signalling completion of the startup.
+         *
+         * <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
+         * thread in order to ensure consistency of the Ikev2VpnRunner fields.
+         */
+        public void onChildOpened(
+                @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) {
+            if (!isActiveNetwork(network)) {
+                Log.d(TAG, "onOpened called for obsolete network " + network);
+
+                // Do nothing; this signals that either: (1) a new/better Network was found,
+                // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+                // IKE session was already shut down (exited, or an error was encountered somewhere
+                // else). In both cases, all resources and sessions are torn down via
+                // resetIkeState().
+                return;
+            }
+
+            try {
+                final String interfaceName = mTunnelIface.getInterfaceName();
+                final int maxMtu = mProfile.getMaxMtu();
+                final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses();
+
+                final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors(
+                        childConfig.getOutboundTrafficSelectors());
+                for (final LinkAddress address : internalAddresses) {
+                    mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+                }
+
+                final NetworkAgent networkAgent;
+                final LinkProperties lp;
+
+                synchronized (Vpn.this) {
+                    mInterface = interfaceName;
+                    mConfig.mtu = maxMtu;
+                    mConfig.interfaze = mInterface;
+
+                    mConfig.addresses.clear();
+                    mConfig.addresses.addAll(internalAddresses);
+
+                    mConfig.routes.clear();
+                    mConfig.routes.addAll(newRoutes);
+
+                    // TODO: Add DNS servers from negotiation
+
+                    networkAgent = mNetworkAgent;
+
+                    // The below must be done atomically with the mConfig update, otherwise
+                    // isRunningLocked() will be racy.
+                    if (networkAgent == null) {
+                        if (isSettingsVpnLocked()) {
+                            prepareStatusIntent();
+                        }
+                        agentConnect();
+                        return; // Link properties are already sent.
+                    }
+
+                    lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked
+                }
+
+                networkAgent.sendLinkProperties(lp);
+            } catch (Exception e) {
+                Log.d(TAG, "Error in ChildOpened for network " + network, e);
+                onSessionLost(network);
+            }
+        }
+
+        /**
+         * Called when an IPsec transform has been created, and should be applied.
+         *
+         * <p>This method is called multiple times over the lifetime of an IkeSession (or default
+         * network), and is MUST always be called on the mExecutor thread in order to ensure
+         * consistency of the Ikev2VpnRunner fields.
+         */
+        public void onChildTransformCreated(
+                @NonNull Network network, @NonNull IpSecTransform transform, int direction) {
+            if (!isActiveNetwork(network)) {
+                Log.d(TAG, "ChildTransformCreated for obsolete network " + network);
+
+                // Do nothing; this signals that either: (1) a new/better Network was found,
+                // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+                // IKE session was already shut down (exited, or an error was encountered somewhere
+                // else). In both cases, all resources and sessions are torn down via
+                // resetIkeState().
+                return;
+            }
+
+            try {
+                // Transforms do not need to be persisted; the IkeSession will keep
+                // them alive for us
+                mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
+            } catch (IOException e) {
+                Log.d(TAG, "Transform application failed for network " + network, e);
+                onSessionLost(network);
+            }
+        }
+
+        /**
+         * Called when a new default network is connected.
+         *
+         * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
+         * state in the process, and starting a new IkeSession instance.
+         *
+         * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is
+         * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the
+         * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
+         */
+        public void onDefaultNetworkChanged(@NonNull Network network) {
+            Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
+
+            // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu
+            // of locking.
+            mExecutor.execute(() -> {
+                try {
+                    if (!mIsRunning) {
+                        Log.d(TAG, "onDefaultNetworkChanged after exit");
+                        return; // VPN has been shut down.
+                    }
+
+                    // Without MOBIKE, we have no way to seamlessly migrate. Close on old
+                    // (non-default) network, and start the new one.
+                    resetIkeState();
+                    mActiveNetwork = network;
+
+                    // TODO(b/149356682): Update this based on new IKE API
+                    mEncapSocket = mIpSecManager.openUdpEncapsulationSocket();
+
+                    // TODO(b/149356682): Update this based on new IKE API
+                    final IkeSessionParams ikeSessionParams =
+                            VpnIkev2Utils.buildIkeSessionParams(mProfile, mEncapSocket);
+                    final ChildSessionParams childSessionParams =
+                            VpnIkev2Utils.buildChildSessionParams();
+
+                    // TODO: Remove the need for adding two unused addresses with
+                    // IPsec tunnels.
+                    mTunnelIface =
+                            mIpSecManager.createIpSecTunnelInterface(
+                                    ikeSessionParams.getServerAddress() /* unused */,
+                                    ikeSessionParams.getServerAddress() /* unused */,
+                                    network);
+                    mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
+
+                    // Socket must be bound to prevent network switches from causing
+                    // the IKE teardown to fail/timeout.
+                    // TODO(b/149356682): Update this based on new IKE API
+                    network.bindSocket(mEncapSocket.getFileDescriptor());
+
+                    mSession = mIkev2SessionCreator.createIkeSession(
+                            mContext,
+                            ikeSessionParams,
+                            childSessionParams,
+                            mExecutor,
+                            new VpnIkev2Utils.IkeSessionCallbackImpl(
+                                    TAG, IkeV2VpnRunner.this, network),
+                            new VpnIkev2Utils.ChildSessionCallbackImpl(
+                                    TAG, IkeV2VpnRunner.this, network));
+                    Log.d(TAG, "Ike Session started for network " + network);
+                } catch (Exception e) {
+                    Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
+                    onSessionLost(network);
+                }
+            });
+        }
+
+        /**
+         * Handles loss of a session
+         *
+         * <p>The loss of a session might be due to an onLost() call, the IKE session getting torn
+         * down for any reason, or an error in updating state (transform application, VPN setup)
+         *
+         * <p>This method MUST always be called on the mExecutor thread in order to ensure
+         * consistency of the Ikev2VpnRunner fields.
+         */
+        public void onSessionLost(@NonNull Network network) {
+            if (!isActiveNetwork(network)) {
+                Log.d(TAG, "onSessionLost() called for obsolete network " + network);
+
+                // Do nothing; this signals that either: (1) a new/better Network was found,
+                // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+                // IKE session was already shut down (exited, or an error was encountered somewhere
+                // else). In both cases, all resources and sessions are torn down via
+                // onSessionLost() and resetIkeState().
+                return;
+            }
+
+            mActiveNetwork = null;
+
+            // Close all obsolete state, but keep VPN alive incase a usable network comes up.
+            // (Mirrors VpnService behavior)
+            Log.d(TAG, "Resetting state for network: " + network);
+
+            synchronized (Vpn.this) {
+                // Since this method handles non-fatal errors only, set mInterface to null to
+                // prevent the NetworkManagementEventObserver from killing this VPN based on the
+                // interface going down (which we expect).
+                mInterface = null;
+                mConfig.interfaze = null;
+
+                // Set as unroutable to prevent traffic leaking while the interface is down.
+                if (mConfig != null && mConfig.routes != null) {
+                    final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes);
+
+                    mConfig.routes.clear();
+                    for (final RouteInfo route : oldRoutes) {
+                        mConfig.routes.add(new RouteInfo(route.getDestination(), RTN_UNREACHABLE));
+                    }
+                    if (mNetworkAgent != null) {
+                        mNetworkAgent.sendLinkProperties(makeLinkProperties());
+                    }
+                }
+            }
+
+            resetIkeState();
+        }
+
+        /**
+         * Cleans up all IKE state
+         *
+         * <p>This method MUST always be called on the mExecutor thread in order to ensure
+         * consistency of the Ikev2VpnRunner fields.
+         */
+        private void resetIkeState() {
+            if (mTunnelIface != null) {
+                // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
+                mTunnelIface.close();
+                mTunnelIface = null;
+            }
+            if (mSession != null) {
+                mSession.kill(); // Kill here to make sure all resources are released immediately
+                mSession = null;
+            }
+
+            // TODO(b/149356682): Update this based on new IKE API
+            if (mEncapSocket != null) {
+                try {
+                    mEncapSocket.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close encap socket", e);
+                }
+                mEncapSocket = null;
+            }
+        }
+
+        /**
+         * Triggers cleanup of outer class' state
+         *
+         * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner.
+         */
+        private void cleanupVpnState() {
+            synchronized (Vpn.this) {
+                agentDisconnect();
+            }
+        }
+
+        /**
+         * Cleans up all Ikev2VpnRunner internal state
+         *
+         * <p>This method MUST always be called on the mExecutor thread in order to ensure
+         * consistency of the Ikev2VpnRunner fields.
+         */
+        private void shutdownVpnRunner() {
+            mActiveNetwork = null;
+            mIsRunning = false;
+
+            resetIkeState();
+
+            final ConnectivityManager cm = ConnectivityManager.from(mContext);
+            cm.unregisterNetworkCallback(mNetworkCallback);
+
+            mExecutor.shutdown();
         }
 
         @Override
         public void exit() {
-            // TODO: Teardown IKE session & any resources.
-            agentDisconnect();
+            // Cleanup outer class' state immediately, otherwise race conditions may ensue.
+            cleanupVpnState();
+
+            mExecutor.execute(() -> {
+                shutdownVpnRunner();
+            });
         }
     }
 
@@ -2488,12 +2850,46 @@
                         throw new IllegalArgumentException("No profile found for " + packageName);
                     }
 
-                    startVpnProfilePrivileged(profile);
+                    startVpnProfilePrivileged(profile, packageName);
                 });
     }
 
-    private void startVpnProfilePrivileged(@NonNull VpnProfile profile) {
-        // TODO: Start PlatformVpnRunner
+    private void startVpnProfilePrivileged(
+            @NonNull VpnProfile profile, @NonNull String packageName) {
+        // Ensure that no other previous instance is running.
+        if (mVpnRunner != null) {
+            mVpnRunner.exit();
+            mVpnRunner = null;
+        }
+        updateState(DetailedState.CONNECTING, "startPlatformVpn");
+
+        try {
+            // Build basic config
+            mConfig = new VpnConfig();
+            mConfig.user = packageName;
+            mConfig.isMetered = profile.isMetered;
+            mConfig.startTime = SystemClock.elapsedRealtime();
+            mConfig.proxyInfo = profile.proxy;
+
+            switch (profile.type) {
+                case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+                case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+                case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+                    mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
+                    mVpnRunner.start();
+                    break;
+                default:
+                    updateState(DetailedState.FAILED, "Invalid platform VPN type");
+                    Log.d(TAG, "Unknown VPN profile type: " + profile.type);
+                    break;
+            }
+        } catch (IOException | GeneralSecurityException e) {
+            // Reset mConfig
+            mConfig = null;
+
+            updateState(DetailedState.FAILED, "VPN startup failed");
+            throw new IllegalArgumentException("VPN startup failed", e);
+        }
     }
 
     /**
@@ -2507,13 +2903,37 @@
     public synchronized void stopVpnProfile(@NonNull String packageName) {
         checkNotNull(packageName, "No package name provided");
 
-        // To stop the VPN profile, the caller must be the current prepared package. Otherwise,
-        // the app is not prepared, and we can just return.
-        if (!isCurrentPreparedPackage(packageName)) {
-            // TODO: Also check to make sure that the running VPN is a VPN profile.
+        // To stop the VPN profile, the caller must be the current prepared package and must be
+        // running an Ikev2VpnProfile.
+        if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) {
             return;
         }
 
         prepareInternal(VpnConfig.LEGACY_VPN);
     }
+
+    /**
+     * Proxy to allow testing
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static class Ikev2SessionCreator {
+        /** Creates a IKE session */
+        public IkeSession createIkeSession(
+                @NonNull Context context,
+                @NonNull IkeSessionParams ikeSessionParams,
+                @NonNull ChildSessionParams firstChildSessionParams,
+                @NonNull Executor userCbExecutor,
+                @NonNull IkeSessionCallback ikeSessionCallback,
+                @NonNull ChildSessionCallback firstChildSessionCallback) {
+            return new IkeSession(
+                    context,
+                    ikeSessionParams,
+                    firstChildSessionParams,
+                    userCbExecutor,
+                    ikeSessionCallback,
+                    firstChildSessionCallback);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
new file mode 100644
index 0000000..33fc32b
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -0,0 +1,390 @@
+/*
+ * 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.connectivity;
+
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
+
+import android.annotation.NonNull;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
+import android.net.Network;
+import android.net.RouteInfo;
+import android.net.eap.EapSessionConfig;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.util.IpRange;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.HexDump;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Utility class to build and convert IKEv2/IPsec parameters.
+ *
+ * @hide
+ */
+public class VpnIkev2Utils {
+    static IkeSessionParams buildIkeSessionParams(
+            @NonNull Ikev2VpnProfile profile, @NonNull UdpEncapsulationSocket socket) {
+        // TODO(b/149356682): Update this based on new IKE API. Only numeric addresses supported
+        //                    until then. All others throw IAE (caught by caller).
+        final InetAddress serverAddr = InetAddresses.parseNumericAddress(profile.getServerAddr());
+        final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
+        final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr());
+
+        // TODO(b/149356682): Update this based on new IKE API.
+        final IkeSessionParams.Builder ikeOptionsBuilder =
+                new IkeSessionParams.Builder()
+                        .setServerAddress(serverAddr)
+                        .setUdpEncapsulationSocket(socket)
+                        .setLocalIdentification(localId)
+                        .setRemoteIdentification(remoteId);
+        setIkeAuth(profile, ikeOptionsBuilder);
+
+        for (final IkeSaProposal ikeProposal : getIkeSaProposals()) {
+            ikeOptionsBuilder.addSaProposal(ikeProposal);
+        }
+
+        return ikeOptionsBuilder.build();
+    }
+
+    static ChildSessionParams buildChildSessionParams() {
+        final TunnelModeChildSessionParams.Builder childOptionsBuilder =
+                new TunnelModeChildSessionParams.Builder();
+
+        for (final ChildSaProposal childProposal : getChildSaProposals()) {
+            childOptionsBuilder.addSaProposal(childProposal);
+        }
+
+        childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET);
+        childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6);
+        childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET);
+        childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6);
+
+        return childOptionsBuilder.build();
+    }
+
+    private static void setIkeAuth(
+            @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) {
+        switch (profile.getType()) {
+            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+                final EapSessionConfig eapConfig =
+                        new EapSessionConfig.Builder()
+                                .setEapMsChapV2Config(profile.getUsername(), profile.getPassword())
+                                .build();
+                builder.setAuthEap(profile.getServerRootCaCert(), eapConfig);
+                break;
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+                builder.setAuthPsk(profile.getPresharedKey());
+                break;
+            case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+                builder.setAuthDigitalSignature(
+                        profile.getServerRootCaCert(),
+                        profile.getUserCert(),
+                        profile.getRsaPrivateKey());
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown auth method set");
+        }
+    }
+
+    private static List<IkeSaProposal> getIkeSaProposals() {
+        // TODO: filter this based on allowedAlgorithms
+        final List<IkeSaProposal> proposals = new ArrayList<>();
+
+        // Encryption Algorithms: Currently only AES_CBC is supported.
+        final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder();
+
+        // Currently only AES_CBC is supported.
+        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+
+        // Authentication/Integrity Algorithms
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+
+        // Add AEAD options
+        final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+        // Add dh, prf for both builders
+        for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
+            builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
+            builder.addDhGroup(DH_GROUP_1024_BIT_MODP);
+            builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
+            builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
+        }
+
+        proposals.add(normalModeBuilder.build());
+        proposals.add(aeadBuilder.build());
+        return proposals;
+    }
+
+    private static List<ChildSaProposal> getChildSaProposals() {
+        // TODO: filter this based on allowedAlgorithms
+        final List<ChildSaProposal> proposals = new ArrayList<>();
+
+        // Add non-AEAD options
+        final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
+
+        // Encryption Algorithms: Currently only AES_CBC is supported.
+        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+
+        // Authentication/Integrity Algorithms
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+
+        // Add AEAD options
+        final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+        proposals.add(normalModeBuilder.build());
+        proposals.add(aeadBuilder.build());
+        return proposals;
+    }
+
+    static class IkeSessionCallbackImpl implements IkeSessionCallback {
+        private final String mTag;
+        private final Vpn.IkeV2VpnRunnerCallback mCallback;
+        private final Network mNetwork;
+
+        IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+            mTag = tag;
+            mCallback = callback;
+            mNetwork = network;
+        }
+
+        @Override
+        public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
+            Log.d(mTag, "IkeOpened for network " + mNetwork);
+            // Nothing to do here.
+        }
+
+        @Override
+        public void onClosed() {
+            Log.d(mTag, "IkeClosed for network " + mNetwork);
+            mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry?
+        }
+
+        @Override
+        public void onClosedExceptionally(@NonNull IkeException exception) {
+            Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
+            mCallback.onSessionLost(mNetwork);
+        }
+
+        @Override
+        public void onError(@NonNull IkeProtocolException exception) {
+            Log.d(mTag, "IkeError for network " + mNetwork, exception);
+            // Non-fatal, log and continue.
+        }
+    }
+
+    static class ChildSessionCallbackImpl implements ChildSessionCallback {
+        private final String mTag;
+        private final Vpn.IkeV2VpnRunnerCallback mCallback;
+        private final Network mNetwork;
+
+        ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+            mTag = tag;
+            mCallback = callback;
+            mNetwork = network;
+        }
+
+        @Override
+        public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
+            Log.d(mTag, "ChildOpened for network " + mNetwork);
+            mCallback.onChildOpened(mNetwork, childConfig);
+        }
+
+        @Override
+        public void onClosed() {
+            Log.d(mTag, "ChildClosed for network " + mNetwork);
+            mCallback.onSessionLost(mNetwork);
+        }
+
+        @Override
+        public void onClosedExceptionally(@NonNull IkeException exception) {
+            Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
+            mCallback.onSessionLost(mNetwork);
+        }
+
+        @Override
+        public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
+            Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork);
+            mCallback.onChildTransformCreated(mNetwork, transform, direction);
+        }
+
+        @Override
+        public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
+            // Nothing to be done; no references to the IpSecTransform are held by the
+            // Ikev2VpnRunner (or this callback class), and this transform will be closed by the
+            // IKE library.
+            Log.d(mTag,
+                    "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork);
+        }
+    }
+
+    static class Ikev2VpnNetworkCallback extends NetworkCallback {
+        private final String mTag;
+        private final Vpn.IkeV2VpnRunnerCallback mCallback;
+
+        Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) {
+            mTag = tag;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onAvailable(@NonNull Network network) {
+            Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
+            mCallback.onDefaultNetworkChanged(network);
+        }
+
+        @Override
+        public void onLost(@NonNull Network network) {
+            Log.d(mTag, "Tearing down; lost network: " + network);
+            mCallback.onSessionLost(network);
+        }
+    }
+
+    /**
+     * Identity parsing logic using similar logic to open source implementations of IKEv2
+     *
+     * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded
+     * identities.
+     */
+    private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) {
+        // TODO: Add identity formatting to public API javadocs.
+        if (identityStr.contains("@")) {
+            if (identityStr.startsWith("@#")) {
+                // KEY_ID
+                final String hexStr = identityStr.substring(2);
+                return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr));
+            } else if (identityStr.startsWith("@@")) {
+                // RFC822 (USER_FQDN)
+                return new IkeRfc822AddrIdentification(identityStr.substring(2));
+            } else if (identityStr.startsWith("@")) {
+                // FQDN
+                return new IkeFqdnIdentification(identityStr.substring(1));
+            } else {
+                // RFC822 (USER_FQDN)
+                return new IkeRfc822AddrIdentification(identityStr);
+            }
+        } else if (InetAddresses.isNumericAddress(identityStr)) {
+            final InetAddress addr = InetAddresses.parseNumericAddress(identityStr);
+            if (addr instanceof Inet4Address) {
+                // IPv4
+                return new IkeIpv4AddrIdentification((Inet4Address) addr);
+            } else if (addr instanceof Inet6Address) {
+                // IPv6
+                return new IkeIpv6AddrIdentification((Inet6Address) addr);
+            } else {
+                throw new IllegalArgumentException("IP version not supported");
+            }
+        } else {
+            if (identityStr.contains(":")) {
+                // KEY_ID
+                return new IkeKeyIdIdentification(identityStr.getBytes());
+            } else {
+                // FQDN
+                return new IkeFqdnIdentification(identityStr);
+            }
+        }
+    }
+
+    static Collection<RouteInfo> getRoutesFromTrafficSelectors(
+            List<IkeTrafficSelector> trafficSelectors) {
+        final HashSet<RouteInfo> routes = new HashSet<>();
+
+        for (final IkeTrafficSelector selector : trafficSelectors) {
+            for (final IpPrefix prefix :
+                    new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) {
+                routes.add(new RouteInfo(prefix, null));
+            }
+        }
+
+        return routes;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 7c2ec78..d9e3025 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -105,32 +105,57 @@
  */
 public class HdmiControlService extends SystemService {
     private static final String TAG = "HdmiControlService";
-    private final Locale HONG_KONG = new Locale("zh", "HK");
-    private final Locale MACAU = new Locale("zh", "MO");
+    private static final Locale HONG_KONG = new Locale("zh", "HK");
+    private static final Locale MACAU = new Locale("zh", "MO");
 
-    private static final Map<String, String> mTerminologyToBibliographicMap;
-    static {
-        mTerminologyToBibliographicMap = new HashMap<>();
+    private static final Map<String, String> sTerminologyToBibliographicMap =
+            createsTerminologyToBibliographicMap();
+
+    private static Map<String, String> createsTerminologyToBibliographicMap() {
+        Map<String, String> temp = new HashMap<>();
         // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
-        mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
-        mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
-        mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
-        mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
-        mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
-        mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
-        mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
-        mTerminologyToBibliographicMap.put("deu", "ger"); // German
-        mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
-        mTerminologyToBibliographicMap.put("fra", "fre"); // French
-        mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
-        mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
-        mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
-        mTerminologyToBibliographicMap.put("msa", "may"); // Malay
-        mTerminologyToBibliographicMap.put("fas", "per"); // Persian
-        mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
-        mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
-        mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
-        mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+        temp.put("sqi", "alb"); // Albanian
+        temp.put("hye", "arm"); // Armenian
+        temp.put("eus", "baq"); // Basque
+        temp.put("mya", "bur"); // Burmese
+        temp.put("ces", "cze"); // Czech
+        temp.put("nld", "dut"); // Dutch
+        temp.put("kat", "geo"); // Georgian
+        temp.put("deu", "ger"); // German
+        temp.put("ell", "gre"); // Greek
+        temp.put("fra", "fre"); // French
+        temp.put("isl", "ice"); // Icelandic
+        temp.put("mkd", "mac"); // Macedonian
+        temp.put("mri", "mao"); // Maori
+        temp.put("msa", "may"); // Malay
+        temp.put("fas", "per"); // Persian
+        temp.put("ron", "rum"); // Romanian
+        temp.put("slk", "slo"); // Slovak
+        temp.put("bod", "tib"); // Tibetan
+        temp.put("cym", "wel"); // Welsh
+        return Collections.unmodifiableMap(temp);
+    }
+
+    @VisibleForTesting static String localeToMenuLanguage(Locale locale) {
+        if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
+            // Android always returns "zho" for all Chinese variants.
+            // Use "bibliographic" code defined in CEC639-2 for traditional
+            // Chinese used in Taiwan/Hong Kong/Macau.
+            return "chi";
+        } else {
+            String language = locale.getISO3Language();
+
+            // locale.getISO3Language() returns terminology code and need to
+            // send it as bibliographic code instead since the Bibliographic
+            // codes of ISO/FDIS 639-2 shall be used.
+            // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+            // But, as it depends on the locale, is not handled here.
+            if (sTerminologyToBibliographicMap.containsKey(language)) {
+                language = sTerminologyToBibliographicMap.get(language);
+            }
+
+            return language;
+        }
     }
 
     static final String PERMISSION = "android.permission.HDMI_CEC";
@@ -208,8 +233,8 @@
                     }
                     break;
                 case Intent.ACTION_CONFIGURATION_CHANGED:
-                    String language = getMenuLanguage();
-                    if (!mLanguage.equals(language)) {
+                    String language = HdmiControlService.localeToMenuLanguage(Locale.getDefault());
+                    if (!mMenuLanguage.equals(language)) {
                         onLanguageChanged(language);
                     }
                     break;
@@ -221,28 +246,6 @@
             }
         }
 
-        private String getMenuLanguage() {
-            Locale locale = Locale.getDefault();
-            if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
-                // Android always returns "zho" for all Chinese variants.
-                // Use "bibliographic" code defined in CEC639-2 for traditional
-                // Chinese used in Taiwan/Hong Kong/Macau.
-                return "chi";
-            } else {
-                String language = locale.getISO3Language();
-
-                // locale.getISO3Language() returns terminology code and need to
-                // send it as bibliographic code instead since the Bibliographic
-                // codes of ISO/FDIS 639-2 shall be used.
-                // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
-                // But, as it depends on the locale, is not handled here.
-                if (mTerminologyToBibliographicMap.containsKey(language)) {
-                    language = mTerminologyToBibliographicMap.get(language);
-                }
-
-                return language;
-            }
-        }
     }
 
     // A thread to handle synchronous IO of CEC and MHL control service.
@@ -339,7 +342,7 @@
     private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
 
     @ServiceThreadOnly
-    private String mLanguage = Locale.getDefault().getISO3Language();
+    private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault());
 
     @ServiceThreadOnly
     private boolean mStandbyMessageReceived = false;
@@ -759,7 +762,7 @@
     private void initializeCec(int initiatedBy) {
         mAddressAllocated = false;
         mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
-        mCecController.setLanguage(mLanguage);
+        mCecController.setLanguage(mMenuLanguage);
         initializeLocalDevices(initiatedBy);
     }
 
@@ -2818,7 +2821,7 @@
     @ServiceThreadOnly
     private void onLanguageChanged(String language) {
         assertRunOnServiceThread();
-        mLanguage = language;
+        mMenuLanguage = language;
 
         if (isTvDeviceEnabled()) {
             tv().broadcastMenuLanguage(language);
@@ -2826,10 +2829,19 @@
         }
     }
 
+    /**
+     * Gets the CEC menu language.
+     *
+     * <p>This is the ISO/FDIS 639-2 3 letter language code sent in the CEC message @{code <Set Menu
+     * Language>}.
+     * See HDMI 1.4b section CEC 13.6.2
+     *
+     * @see {@link Locale#getISO3Language()}
+     */
     @ServiceThreadOnly
     String getLanguage() {
         assertRunOnServiceThread();
-        return mLanguage;
+        return mMenuLanguage;
     }
 
     private void disableDevices(PendingActionClearedCallback callback) {
@@ -2838,7 +2850,6 @@
                 device.disableDevice(mStandbyMessageReceived, callback);
             }
         }
-
         mMhlController.clearAllLocalDevices();
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
new file mode 100644
index 0000000..25ef9fa
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+
+ogunwale@google.com
+yukawa@google.com
+tarandeep@google.com
+lumark@google.com
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 68ced79..b9a30bb 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -23,6 +23,7 @@
 import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
 import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
 import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
+import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
 import static android.content.integrity.IntegrityUtils.getHexDigest;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
 
@@ -95,7 +96,7 @@
      * This string will be used as the "installer" for formula evaluation when the app is being
      * installed via ADB.
      */
-    private static final String ADB_INSTALLER = "adb";
+    public static final String ADB_INSTALLER = "adb";
 
     private static final String TAG = "AppIntegrityManagerServiceImpl";
 
@@ -106,8 +107,6 @@
     private static final String ALLOWED_INSTALLER_DELIMITER = ",";
     private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
 
-    private static final String INSTALLER_CERT_NOT_APPLICABLE = "";
-
     // Access to files inside mRulesDir is protected by mRulesLock;
     private final Context mContext;
     private final Handler mHandler;
@@ -282,15 +281,16 @@
             builder.setInstallerName(getPackageNameNormalized(installerPackageName));
             builder.setInstallerCertificates(installerCertificates);
             builder.setIsPreInstalled(isSystemApp(packageName));
+            builder.setAllowedInstallersAndCert(getAllowedInstallers(packageInfo));
 
             AppInstallMetadata appInstallMetadata = builder.build();
-            Map<String, String> allowedInstallers = getAllowedInstallers(packageInfo);
 
             Slog.i(
                     TAG,
-                    "To be verified: " + appInstallMetadata + " installers " + allowedInstallers);
+                    "To be verified: " + appInstallMetadata + " installers " + getAllowedInstallers(
+                            packageInfo));
             IntegrityCheckResult result =
-                    mEvaluationEngine.evaluate(appInstallMetadata, allowedInstallers);
+                    mEvaluationEngine.evaluate(appInstallMetadata);
             Slog.i(
                     TAG,
                     "Integrity check result: "
@@ -449,9 +449,9 @@
                         String packageName = getPackageNameNormalized(packageAndCert[0]);
                         String cert = packageAndCert[1];
                         packageCertMap.put(packageName, cert);
-                    } else if (packageAndCert.length == 1
-                            && packageAndCert[0].equals(ADB_INSTALLER)) {
-                        packageCertMap.put(ADB_INSTALLER, INSTALLER_CERT_NOT_APPLICABLE);
+                    } else if (packageAndCert.length == 1) {
+                        packageCertMap.put(getPackageNameNormalized(packageAndCert[0]),
+                                INSTALLER_CERTIFICATE_NOT_EVALUATED);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index 79e69e1..61da45d 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -17,9 +17,6 @@
 package com.android.server.integrity.engine;
 
 import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityFormula;
 import android.content.integrity.Rule;
 import android.util.Slog;
 
@@ -28,10 +25,8 @@
 import com.android.server.integrity.model.IntegrityCheckResult;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
 
 /**
  * The engine used to evaluate rules against app installs.
@@ -69,16 +64,15 @@
      * @return result of the integrity check
      */
     public IntegrityCheckResult evaluate(
-            AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallers) {
+            AppInstallMetadata appInstallMetadata) {
         List<Rule> rules = loadRules(appInstallMetadata);
-        allowedInstallersRule(allowedInstallers).ifPresent(rules::add);
         return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
     }
 
     private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
         if (!mIntegrityFileManager.initialized()) {
-            Slog.w(TAG, "Integrity rule files are not available. Evaluating only manifest rules.");
-            return new ArrayList<>();
+            Slog.w(TAG, "Integrity rule files are not available.");
+            return Collections.emptyList();
         }
 
         try {
@@ -88,41 +82,4 @@
             return new ArrayList<>();
         }
     }
-
-    private static Optional<Rule> allowedInstallersRule(Map<String, String> allowedInstallers) {
-        if (allowedInstallers.isEmpty()) {
-            return Optional.empty();
-        }
-
-        List<IntegrityFormula> formulas = new ArrayList<>(allowedInstallers.size());
-        allowedInstallers.forEach(
-                (installer, cert) -> {
-                    formulas.add(allowedInstallerFormula(installer, cert));
-                });
-
-        // We need this special case since OR-formulas require at least two operands.
-        IntegrityFormula allInstallersFormula =
-                formulas.size() == 1
-                        ? formulas.get(0)
-                        : new CompoundFormula(CompoundFormula.OR, formulas);
-
-        return Optional.of(
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.NOT, Arrays.asList(allInstallersFormula)),
-                        Rule.DENY));
-    }
-
-    private static IntegrityFormula allowedInstallerFormula(String installer, String cert) {
-        return new CompoundFormula(
-                CompoundFormula.AND,
-                Arrays.asList(
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.INSTALLER_NAME,
-                                installer,
-                                /* isHashedValue= */ false),
-                        new AtomicFormula.StringAtomicFormula(
-                                AtomicFormula.INSTALLER_CERTIFICATE, cert, /* isHashedValue= */
-                                false)));
-    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c214082..d0d0f5a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -932,7 +932,7 @@
                 StatusBarNotification sbn = r.getSbn();
                 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
                         sbn.getId(), Notification.FLAG_AUTO_CANCEL,
-                        FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
+                        FLAG_FOREGROUND_SERVICE | FLAG_BUBBLE, false, r.getUserId(),
                         REASON_CLICK, nv.rank, nv.count, null);
                 nv.recycle();
                 reportUserInteraction(r);
@@ -1055,7 +1055,7 @@
                         if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
                         reportSeen(r);
                     }
-                    r.setVisibility(true, nv.rank, nv.count);
+                    r.setVisibility(true, nv.rank, nv.count, mNotificationRecordLogger);
                     mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true);
                     boolean isHun = (nv.location
                             == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
@@ -1074,7 +1074,7 @@
                 for (NotificationVisibility nv : noLongerVisibleKeys) {
                     NotificationRecord r = mNotificationsByKey.get(nv.key);
                     if (r == null) continue;
-                    r.setVisibility(false, nv.rank, nv.count);
+                    r.setVisibility(false, nv.rank, nv.count, mNotificationRecordLogger);
                     mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false);
                     nv.recycle();
                 }
@@ -6491,7 +6491,7 @@
                         mUsageStats.registerPostedByApp(r);
                         r.setInterruptive(isVisuallyInterruptive(null, r));
                     } else {
-                        old = mNotificationList.get(index);
+                        old = mNotificationList.get(index);  // Potentially *changes* old
                         mNotificationList.set(index, r);
                         mUsageStats.registerUpdatedByApp(r, old);
                         // Make sure we don't lose the foreground service state.
@@ -6560,7 +6560,7 @@
                     maybeRecordInterruptionLocked(r);
 
                     // Log event to statsd
-                    mNotificationRecordLogger.logNotificationReported(r, old, position,
+                    mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position,
                             buzzBeepBlinkLoggingCode);
                 } finally {
                     int N = mEnqueuedNotifications.size();
@@ -8009,7 +8009,8 @@
 
                     FlagChecker flagChecker = (int flags) -> {
                         int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
-                        if (REASON_LISTENER_CANCEL_ALL == reason) {
+                        if (REASON_LISTENER_CANCEL_ALL == reason
+                                || REASON_CANCEL_ALL == reason) {
                             flagsToCheck |= FLAG_BUBBLE;
                         }
                         if ((flags & flagsToCheck) != 0) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 4785da9..0ada58e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -909,7 +909,8 @@
     /**
      * Set the visibility of the notification.
      */
-    public void setVisibility(boolean visible, int rank, int count) {
+    public void setVisibility(boolean visible, int rank, int count,
+            NotificationRecordLogger notificationRecordLogger) {
         final long now = System.currentTimeMillis();
         mVisibleSinceMs = visible ? now : mVisibleSinceMs;
         stats.onVisibilityChanged(visible);
@@ -927,6 +928,7 @@
                 getFreshnessMs(now),
                 0, // exposure time
                 rank);
+        notificationRecordLogger.logNotificationVisibility(this, visible);
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 2f78542..eaca066f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -41,13 +41,14 @@
 public interface NotificationRecordLogger {
 
     /**
-     * Logs a NotificationReported atom reflecting the posting or update of a notification.
+     * May log a NotificationReported atom reflecting the posting or update of a notification.
      * @param r The new NotificationRecord. If null, no action is taken.
      * @param old The previous NotificationRecord.  Null if there was no previous record.
      * @param position The position at which this notification is ranked.
      * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user.
      */
-    void logNotificationReported(@Nullable NotificationRecord r, @Nullable NotificationRecord old,
+    void maybeLogNotificationPosted(@Nullable NotificationRecord r,
+            @Nullable NotificationRecord old,
             int position, int buzzBeepBlink);
 
     /**
@@ -62,6 +63,14 @@
             @NotificationStats.DismissalSurface int dismissalSurface);
 
     /**
+     * Logs a notification visibility change event using UiEventReported (event ids from the
+     * NotificationEvents enum).
+     * @param r The NotificationRecord. If null, no action is taken.
+     * @param visible True if the notification became visible.
+     */
+    void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible);
+
+    /**
      * The UiEvent enums that this class can log.
      */
     enum NotificationReportedEvent implements UiEventLogger.UiEventEnum {
@@ -145,6 +154,7 @@
         @Override public int getId() {
             return mId;
         }
+
         public static NotificationCancelledEvent fromCancelReason(
                 @NotificationListenerService.NotificationCancelReason int reason,
                 @NotificationStats.DismissalSurface int surface) {
@@ -191,6 +201,24 @@
         }
     }
 
+    enum NotificationEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Notification became visible.")
+        NOTIFICATION_OPEN(197),
+        @UiEvent(doc = "Notification stopped being visible.")
+        NOTIFICATION_CLOSE(198);
+
+        private final int mId;
+        NotificationEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+
+        public static NotificationEvent fromVisibility(boolean visible) {
+            return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE;
+        }
+    }
     /**
      * A helper for extracting logging information from one or two NotificationRecords.
      */
@@ -209,7 +237,7 @@
         /**
          * @return True if old is null, alerted, or important logged fields have changed.
          */
-        boolean shouldLog(int buzzBeepBlink) {
+        boolean shouldLogReported(int buzzBeepBlink) {
             if (r == null) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index bb23d1e..9fcac25 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -26,13 +26,13 @@
  */
 public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
 
-    UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+    private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
     @Override
-    public void logNotificationReported(NotificationRecord r, NotificationRecord old,
+    public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
             int position, int buzzBeepBlink) {
         NotificationRecordPair p = new NotificationRecordPair(r, old);
-        if (!p.shouldLog(buzzBeepBlink)) {
+        if (!p.shouldLogReported(buzzBeepBlink)) {
             return;
         }
         FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
@@ -66,8 +66,19 @@
 
     @Override
     public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
-        mUiEventLogger.logWithInstanceId(
-                NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface),
-                r.getUid(), r.getSbn().getPackageName(), r.getSbn().getInstanceId());
+        log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
+    }
+
+    @Override
+    public void logNotificationVisibility(NotificationRecord r, boolean visible) {
+        log(NotificationEvent.fromVisibility(visible), r);
+    }
+
+    void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+        if (r == null) {
+            return;
+        }
+        mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(),
+                r.getSbn().getInstanceId());
     }
 }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index d629b54..0fb889c 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -69,7 +69,7 @@
     // Logs all filtering instead of enforcing
     private static final boolean DEBUG_ALLOW_ALL = false;
     private static final boolean DEBUG_LOGGING = false;
-    private static final boolean FEATURE_ENABLED_BY_DEFAULT = false;
+    private static final boolean FEATURE_ENABLED_BY_DEFAULT = true;
 
     /**
      * This contains a list of app UIDs that are implicitly queryable because another app explicitly
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b98bb08..8ad3e9d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -611,10 +611,10 @@
     /**
      * Bind mount private volume CE and DE mirror storage.
      */
-    public void onPrivateVolumeMounted(String volumeUuid) throws InstallerException {
+    public void tryMountDataMirror(String volumeUuid) throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.onPrivateVolumeMounted(volumeUuid);
+            mInstalld.tryMountDataMirror(volumeUuid);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2c85d06..064fd3f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4600,7 +4600,7 @@
         synchronized (mLock) {
             final AndroidPackage p = mPackages.get(packageName);
             if (p != null && p.isMatch(flags)) {
-                PackageSetting ps = getPackageSetting(p.getPackageName());
+                PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
                 if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return -1;
                 }
@@ -5924,7 +5924,10 @@
      */
     @Override
     public String[] getPackagesForUid(int uid) {
-        final int callingUid = Binder.getCallingUid();
+        return getPackagesForUidInternal(uid, Binder.getCallingUid());
+    }
+
+    private String[] getPackagesForUidInternal(int uid, int callingUid) {
         final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
         final int userId = UserHandle.getUserId(uid);
         final int appId = UserHandle.getAppId(uid);
@@ -17380,6 +17383,13 @@
 
     @GuardedBy("mLock")
     private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+        final int callingUid = Binder.getCallingUid();
+        return resolveInternalPackageNameInternalLocked(packageName, versionCode,
+                callingUid);
+    }
+
+    private String resolveInternalPackageNameInternalLocked(
+            String packageName, long versionCode, int callingUid) {
         // Handle renamed packages
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
         packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
@@ -17393,12 +17403,12 @@
 
         // Figure out which lib versions the caller can see
         LongSparseLongArray versionsCallerCanSee = null;
-        final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+        final int callingAppId = UserHandle.getAppId(callingUid);
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
                 && callingAppId != Process.ROOT_UID) {
             versionsCallerCanSee = new LongSparseLongArray();
             String libName = versionedLib.valueAt(0).getName();
-            String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
+            String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
             if (uidPackages != null) {
                 for (String uidPackage : uidPackages) {
                     PackageSetting ps = mSettings.getPackageLPr(uidPackage);
@@ -23003,7 +23013,7 @@
         @Override
         public AndroidPackage getPackage(int uid) {
             synchronized (mLock) {
-                final String[] packageNames = getPackagesForUid(uid);
+                final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
                 AndroidPackage pkg = null;
                 final int numPackages = packageNames == null ? 0 : packageNames.length;
                 for (int i = 0; pkg == null && i < numPackages; i++) {
@@ -24017,9 +24027,13 @@
 
     @Nullable
     public PackageSetting getPackageSetting(String packageName) {
+        return getPackageSettingInternal(packageName, Binder.getCallingUid());
+    }
+
+    private PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
         synchronized (mLock) {
-            packageName = resolveInternalPackageNameLPr(
-                    packageName, PackageManager.VERSION_CODE_HIGHEST);
+            packageName = resolveInternalPackageNameInternalLocked(
+                    packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
             return mSettings.mPackages.get(packageName);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c267cea..f1e403b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -119,6 +119,7 @@
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -135,7 +136,6 @@
 class PackageManagerShellCommand extends ShellCommand {
     /** Path for streaming APK content */
     private static final String STDIN_PATH = "-";
-    private static final byte[] STDIN_PATH_BYTES = "-".getBytes(StandardCharsets.UTF_8);
     /** Path where ART profiles snapshots are dumped for the shell user */
     private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
     private static final int DEFAULT_WAIT_MS = 60 * 1000;
@@ -2988,8 +2988,10 @@
         try {
             // 1. Single file from stdin.
             if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
-                String name = "base." + (isApex ? "apex" : "apk");
-                session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes, STDIN_PATH_BYTES, null);
+                final String name = "base." + (isApex ? "apex" : "apk");
+                final String metadata = "-" + name;
+                session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
+                        metadata.getBytes(StandardCharsets.UTF_8), null);
                 return 0;
             }
 
@@ -2998,24 +3000,58 @@
 
                 // 2. File with specified size read from stdin.
                 if (delimLocation != -1) {
-                    String name = arg.substring(0, delimLocation);
-                    String sizeStr = arg.substring(delimLocation + 1);
-                    long sizeBytes;
+                    final String[] fileDesc = arg.split(":");
+                    String name = null;
+                    long sizeBytes = -1;
+                    String metadata;
+                    byte[] signature = null;
+
+                    try {
+                        if (fileDesc.length > 0) {
+                            name = fileDesc[0];
+                        }
+                        if (fileDesc.length > 1) {
+                            sizeBytes = Long.parseUnsignedLong(fileDesc[1]);
+                        }
+                        if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) {
+                            metadata = fileDesc[2];
+                        } else {
+                            metadata = name;
+                        }
+                        if (fileDesc.length > 3) {
+                            signature = Base64.getDecoder().decode(fileDesc[3]);
+                        }
+                    } catch (IllegalArgumentException e) {
+                        getErrPrintWriter().println(
+                                "Unable to parse file parameters: " + arg + ", reason: " + e);
+                        return 1;
+                    }
 
                     if (TextUtils.isEmpty(name)) {
                         getErrPrintWriter().println("Empty file name in: " + arg);
                         return 1;
                     }
+
+                    if (signature != null) {
+                        // Streaming/adb mode.
+                        metadata = "+" + metadata;
+                    } else {
+                        // Singleshot read from stdin.
+                        metadata = "-" + metadata;
+                    }
+
                     try {
-                        sizeBytes = Long.parseUnsignedLong(sizeStr);
-                    } catch (NumberFormatException e) {
-                        getErrPrintWriter().println("Unable to parse size from: " + arg);
+                        if (V4Signature.readFrom(signature) == null) {
+                            getErrPrintWriter().println("V4 signature is invalid in: " + arg);
+                            return 1;
+                        }
+                    } catch (Exception e) {
+                        getErrPrintWriter().println("V4 signature is invalid: " + e + " in " + arg);
                         return 1;
                     }
 
-                    // Incremental requires unique metadatas, let's add a name to the dash.
                     session.addFile(LOCATION_DATA_APP, name, sizeBytes,
-                            ("-" + name).getBytes(StandardCharsets.UTF_8), null);
+                            metadata.getBytes(StandardCharsets.UTF_8), signature);
                     continue;
                 }
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0fb4cb0..832c9b7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4393,6 +4393,7 @@
             ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT, "SYSTEM_EXT",
             ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD",
             ApplicationInfo.PRIVATE_FLAG_ODM, "ODM",
+            ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING, "PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING",
     };
 
     void dumpVersionLPr(IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 67a22d3..39093ae 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -866,6 +866,10 @@
         }
     }
 
+    default int getMaxWindowLayer() {
+        return 35;
+    }
+
     /**
      * Return how to Z-order sub-windows in relation to the window they are attached to.
      * Return positive to have them ordered in front, negative for behind.
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9e150fd..f9981d0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -802,7 +802,6 @@
             newRollback = getRollbackForSessionLocked(packageSession.getSessionId());
             if (newRollback == null) {
                 newRollback = createNewRollbackLocked(parentSession);
-                mRollbacks.add(newRollback);
             }
         }
         newRollback.addToken(token);
@@ -1002,11 +1001,10 @@
                 }
             }
 
-            Rollback rollback = completeEnableRollback(newRollback);
-            if (rollback == null) {
+            if (!completeEnableRollback(newRollback)) {
                 result.offer(-1);
             } else {
-                result.offer(rollback.info.getRollbackId());
+                result.offer(newRollback.info.getRollbackId());
             }
         });
 
@@ -1158,19 +1156,10 @@
                 Rollback rollback;
                 synchronized (mLock) {
                     rollback = getRollbackForSessionLocked(sessionId);
-                    if (rollback == null || rollback.isStaged() || !rollback.isEnabling()
-                            || !rollback.notifySessionWithSuccess()) {
-                        return;
-                    }
-                    // All child sessions finished with success. We can enable this rollback now.
-                    // TODO: refactor #completeEnableRollback so we won't remove 'rollback' from
-                    // mRollbacks here and add it back in #completeEnableRollback later.
-                    mRollbacks.remove(rollback);
                 }
-                // TODO: Now #completeEnableRollback returns the same rollback object as the
-                // parameter on success. It would be more readable to return a boolean to indicate
-                // success or failure.
-                if (completeEnableRollback(rollback) != null) {
+                if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
+                        && rollback.notifySessionWithSuccess()
+                        && completeEnableRollback(rollback)) {
                     makeRollbackAvailable(rollback);
                 }
             } else {
@@ -1188,13 +1177,14 @@
     }
 
     /**
-     * Add a rollback to the list of rollbacks. It does not make the rollback available yet.
+     * Persist a rollback as enable-completed. It does not make the rollback available yet.
+     * This rollback will be deleted and removed from {@link #mRollbacks} should any error happens.
      *
-     * @return the Rollback instance for a successfully enable-completed rollback,
-     * or null on error.
+     * @return {code true} if {code rollback} is successfully enable-completed,
+     * or {code false} otherwise.
      */
     @WorkerThread
-    private Rollback completeEnableRollback(Rollback rollback) {
+    private boolean completeEnableRollback(Rollback rollback) {
         if (LOCAL_LOGV) {
             Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
         }
@@ -1205,26 +1195,24 @@
         // rollback for the embedded apk-in-apex, if any.
         if (!rollback.allPackagesEnabled()) {
             Slog.e(TAG, "Failed to enable rollback for all packages in session.");
+            mRollbacks.remove(rollback);
             rollback.delete(mAppDataRollbackHelper);
-            return null;
+            return false;
         }
 
+        // Note: There is a small window of time between when
+        // the session has been committed by the package
+        // manager and when we make the rollback available
+        // here. Presumably the window is small enough that
+        // nobody will want to roll back the newly installed
+        // package before we make the rollback available.
+        // TODO: We'll lose the rollback if the
+        // device reboots between when the session is
+        // committed and this point. Revisit this after
+        // adding support for rollback of staged installs.
         rollback.saveRollback();
-        synchronized (mLock) {
-            // Note: There is a small window of time between when
-            // the session has been committed by the package
-            // manager and when we make the rollback available
-            // here. Presumably the window is small enough that
-            // nobody will want to roll back the newly installed
-            // package before we make the rollback available.
-            // TODO: We'll lose the rollback if the
-            // device reboots between when the session is
-            // committed and this point. Revisit this after
-            // adding support for rollback of staged installs.
-            mRollbacks.add(rollback);
-        }
 
-        return rollback;
+        return true;
     }
 
     @WorkerThread
@@ -1304,6 +1292,10 @@
         }
     }
 
+    /**
+     * Creates and returns a Rollback according to the given SessionInfo
+     * and adds it to {@link #mRollbacks}.
+     */
     @WorkerThread
     @GuardedBy("mLock")
     private Rollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
@@ -1338,6 +1330,7 @@
                     installerPackageName, packageSessionIds);
         }
 
+        mRollbacks.add(rollback);
         return rollback;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b3edc91..0d365b1 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -102,6 +102,11 @@
     }
 
     @Override
+    public String toString() {
+        return mName + "@" + System.identityHashCode(this);
+    }
+
+    @Override
     public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 9e93e14..0ec0c7b 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
 import android.content.res.Resources;
 import android.text.TextUtils;
 
@@ -43,7 +41,7 @@
     /**
      * The Tasks container. Tasks etc. are automatically added to this container.
      */
-    protected final TaskContainers mTaskContainers;
+    protected final DisplayArea<? extends ActivityStack> mTaskContainers;
 
     /**
      * Construct a new {@link DisplayAreaPolicy}
@@ -58,7 +56,8 @@
      */
     protected DisplayAreaPolicy(WindowManagerService wmService,
             DisplayContent content, DisplayArea.Root root,
-            DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
+            DisplayArea<? extends WindowContainer> imeContainer,
+            DisplayArea<? extends ActivityStack> taskContainers) {
         mWmService = wmService;
         mContent = content;
         mRoot = root;
@@ -83,67 +82,15 @@
      */
     public abstract void addWindow(WindowToken token);
 
-    /**
-     * Default policy that has no special features.
-     */
-    public static class Default extends DisplayAreaPolicy {
-
-        public Default(WindowManagerService wmService, DisplayContent content,
-                DisplayArea.Root root,
+    /** Provider for platform-default display area policy. */
+    static final class DefaultProvider implements DisplayAreaPolicy.Provider {
+        @Override
+        public DisplayAreaPolicy instantiate(WindowManagerService wmService,
+                DisplayContent content, DisplayArea.Root root,
                 DisplayArea<? extends WindowContainer> imeContainer,
                 TaskContainers taskContainers) {
-            super(wmService, content, root, imeContainer, taskContainers);
-        }
-
-        private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService,
-                DisplayArea.Type.BELOW_TASKS, "BelowTasks");
-        private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService,
-                DisplayArea.Type.ABOVE_TASKS, "AboveTasks");
-        private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService,
-                DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme");
-        private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService,
-                DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme");
-
-        @Override
-        public void attachDisplayAreas() {
-            mRoot.addChild(mBelow, 0);
-            mRoot.addChild(mTaskContainers, 1);
-            mRoot.addChild(mAbove, 2);
-
-            mAbove.addChild(mAboveBelowIme, 0);
-            mAbove.addChild(mImeContainer, 1);
-            mAbove.addChild(mAboveAboveIme, 2);
-        }
-
-        @Override
-        public void addWindow(WindowToken token) {
-            switch (DisplayArea.Type.typeOf(token)) {
-                case ABOVE_TASKS:
-                    if (token.getWindowLayerFromType()
-                            < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) {
-                        mAboveBelowIme.addChild(token);
-                    } else {
-                        mAboveAboveIme.addChild(token);
-                    }
-                    break;
-                case BELOW_TASKS:
-                    mBelow.addChild(token);
-                    break;
-                default:
-                    throw new IllegalArgumentException("don't know how to sort " + token);
-            }
-        }
-
-        /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */
-        static class Provider implements DisplayAreaPolicy.Provider {
-            @Override
-            public DisplayAreaPolicy instantiate(WindowManagerService wmService,
-                    DisplayContent content, DisplayArea.Root root,
-                    DisplayArea<? extends WindowContainer> imeContainer,
-                    TaskContainers taskContainers) {
-                return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer,
-                        taskContainers);
-            }
+            return new DisplayAreaPolicyBuilder()
+                    .build(wmService, content, root, imeContainer, taskContainers);
         }
     }
 
@@ -172,7 +119,7 @@
             String name = res.getString(
                     com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
             if (TextUtils.isEmpty(name)) {
-                return new DisplayAreaPolicy.Default.Provider();
+                return new DisplayAreaPolicy.DefaultProvider();
             }
             try {
                 return (Provider) Class.forName(name).newInstance();
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
new file mode 100644
index 0000000..885456a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -0,0 +1,371 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A builder for instantiating a complex {@link DisplayAreaPolicy}
+ *
+ * <p>Given a set of features (that each target a set of window types), it builds the necessary
+ * DisplayArea hierarchy.
+ *
+ * <p>Example: <br />
+ *
+ * <pre>
+ *     // Feature for targeting everything below the magnification overlay:
+ *     new DisplayAreaPolicyBuilder(...)
+ *             .addFeature(new Feature.Builder(..., "Magnification")
+ *                     .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ *                     .build())
+ *             .build(...)
+ *
+ *     // Builds a policy with the following hierarchy:
+ *      - DisplayArea.Root
+ *        - Magnification
+ *          - DisplayArea.Tokens (Wallpapers are attached here)
+ *          - TaskContainers
+ *          - DisplayArea.Tokens (windows above Tasks up to IME are attached here)
+ *          - ImeContainers
+ *          - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here)
+ *        - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here)
+ *
+ * </pre>
+ *
+ * // TODO(display-area): document more complex scenarios where we need multiple areas per feature.
+ */
+class DisplayAreaPolicyBuilder {
+
+    private final ArrayList<Feature> mFeatures = new ArrayList<>();
+
+    /**
+     * A feature that requires {@link DisplayArea DisplayArea(s)}.
+     */
+    static class Feature {
+        private final String mName;
+        private final boolean[] mWindowLayers;
+
+        private Feature(String name, boolean[] windowLayers) {
+            mName = name;
+            mWindowLayers = windowLayers;
+        }
+
+        static class Builder {
+            private final WindowManagerPolicy mPolicy;
+            private final String mName;
+            private final boolean[] mLayers;
+
+            /**
+             * Build a new feature that applies to a set of window types as specified by the builder
+             * methods.
+             *
+             * <p>The set of types is updated iteratively in the order of the method invocations.
+             * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
+             * apply to all types except TYPE_STATUS_BAR.
+             *
+             * The builder starts out with the feature not applying to any types.
+             *
+             * @param name the name of the feature.
+             */
+            Builder(WindowManagerPolicy policy, String name) {
+                mPolicy = policy;
+                mName = name;
+                mLayers = new boolean[mPolicy.getMaxWindowLayer()];
+            }
+
+            /**
+             * Set that the feature applies to all window types.
+             */
+            Builder all() {
+                Arrays.fill(mLayers, true);
+                return this;
+            }
+
+            /**
+             * Set that the feature applies to the given window types.
+             */
+            Builder and(int... types) {
+                for (int i = 0; i < types.length; i++) {
+                    int type = types[i];
+                    set(type, true);
+                }
+                return this;
+            }
+
+            /**
+             * Set that the feature does not apply to the given window types.
+             */
+            Builder except(int... types) {
+                for (int i = 0; i < types.length; i++) {
+                    int type = types[i];
+                    set(type, false);
+                }
+                return this;
+            }
+
+            /**
+             * Set that the feature applies window types that are layerd at or below the layer of
+             * the given window type.
+             */
+            Builder upTo(int typeInclusive) {
+                final int max = layerFromType(typeInclusive, false);
+                for (int i = 0; i < max; i++) {
+                    mLayers[i] = true;
+                }
+                set(typeInclusive, true);
+                return this;
+            }
+
+            Feature build() {
+                return new Feature(mName, mLayers.clone());
+            }
+
+            private void set(int type, boolean value) {
+                mLayers[layerFromType(type, true)] = value;
+                if (type == TYPE_APPLICATION_OVERLAY) {
+                    mLayers[layerFromType(type, true)] = value;
+                    mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value;
+                    mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value;
+                    mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value;
+                }
+            }
+
+            private int layerFromType(int type, boolean internalWindows) {
+                return mPolicy.getWindowLayerFromTypeLw(type, internalWindows);
+            }
+        }
+    }
+
+    static class Result extends DisplayAreaPolicy {
+        private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
+        private static final int LEAF_TYPE_IME_CONTAINERS = 2;
+        private static final int LEAF_TYPE_TOKENS = 0;
+
+        private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer();
+
+        private final ArrayList<Feature> mFeatures;
+        private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas;
+        private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer];
+
+        Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
+                DisplayArea<? extends WindowContainer> imeContainer,
+                DisplayArea<? extends ActivityStack> taskStacks, ArrayList<Feature> features) {
+            super(wmService, content, root, imeContainer, taskStacks);
+            mFeatures = features;
+            mAreas = new HashMap<>(features.size());
+            for (int i = 0; i < mFeatures.size(); i++) {
+                mAreas.put(mFeatures.get(i), new ArrayList<>());
+            }
+        }
+
+        @Override
+        public void attachDisplayAreas() {
+            // This method constructs the layer hierarchy with the following properties:
+            // (1) Every feature maps to a set of DisplayAreas
+            // (2) After adding a window, for every feature the window's type belongs to,
+            //     it is a descendant of one of the corresponding DisplayAreas of the feature.
+            // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
+            //     within a DisplayArea:
+            //      for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
+            //      max(z-range(a)) <= min(z-range(b))
+            //
+            // The algorithm below iteratively creates such a hierarchy:
+            //  - Initially, all windows are attached to the root.
+            //  - For each feature we create a set of DisplayAreas, by looping over the layers
+            //    - if the feature does apply to the current layer, we need to find a DisplayArea
+            //      for it to satisfy (2)
+            //      - we can re-use the previous layer's area if:
+            //         the current feature also applies to the previous layer, (to satisfy (3))
+            //         and the last feature that applied to the previous layer is the same as
+            //           the last feature that applied to the current layer (to satisfy (2))
+            //      - otherwise we create a new DisplayArea below the last feature that applied
+            //        to the current layer
+
+
+            PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer];
+            final PendingArea root = new PendingArea(null, 0, null);
+            Arrays.fill(areaForLayer, root);
+
+            final int size = mFeatures.size();
+            for (int i = 0; i < size; i++) {
+                PendingArea featureArea = null;
+                for (int layer = 0; layer < mMaxWindowLayer; layer++) {
+                    final Feature feature = mFeatures.get(i);
+                    if (feature.mWindowLayers[layer]) {
+                        if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
+                            // No suitable DisplayArea - create a new one under the previous area
+                            // for this layer.
+                            featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
+                            areaForLayer[layer].mChildren.add(featureArea);
+                        }
+                        areaForLayer[layer] = featureArea;
+                    } else {
+                        featureArea = null;
+                    }
+                }
+            }
+
+            PendingArea leafArea = null;
+            int leafType = LEAF_TYPE_TOKENS;
+            for (int layer = 0; layer < mMaxWindowLayer; layer++) {
+                int type = typeOfLayer(mWmService.mPolicy, layer);
+                if (leafArea == null || leafArea.mParent != areaForLayer[layer]
+                        || type != leafType) {
+                    leafArea = new PendingArea(null, layer, areaForLayer[layer]);
+                    areaForLayer[layer].mChildren.add(leafArea);
+                    leafType = type;
+                    if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
+                        leafArea.mExisting = mTaskContainers;
+                    } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
+                        leafArea.mExisting = mImeContainer;
+                    }
+                }
+                leafArea.mMaxLayer = layer;
+            }
+            root.computeMaxLayer();
+            root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
+        }
+
+        @Override
+        public void addWindow(WindowToken token) {
+            DisplayArea.Tokens area = findAreaForToken(token);
+            area.addChild(token);
+        }
+
+        @VisibleForTesting
+        DisplayArea.Tokens findAreaForToken(WindowToken token) {
+            int windowLayerFromType = token.getWindowLayerFromType();
+            if (windowLayerFromType == APPLICATION_LAYER) {
+                // TODO(display-area): Better handle AboveAppWindows in APPLICATION_LAYER
+                windowLayerFromType += 1;
+            } else if (token.mRoundedCornerOverlay) {
+                windowLayerFromType = mMaxWindowLayer - 1;
+            }
+            return mAreaForLayer[windowLayerFromType];
+        }
+
+        public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) {
+            return mAreas.get(feature);
+        }
+
+        private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
+            if (layer == APPLICATION_LAYER) {
+                return LEAF_TYPE_TASK_CONTAINERS;
+            } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
+                    || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
+                return LEAF_TYPE_IME_CONTAINERS;
+            } else {
+                return LEAF_TYPE_TOKENS;
+            }
+        }
+    }
+
+    DisplayAreaPolicyBuilder addFeature(Feature feature) {
+        mFeatures.add(feature);
+        return this;
+    }
+
+    Result build(WindowManagerService wmService,
+            DisplayContent content, DisplayArea.Root root,
+            DisplayArea<? extends WindowContainer> imeContainer,
+            DisplayArea<? extends ActivityStack> taskContainers) {
+
+        return new Result(wmService, content, root, imeContainer, taskContainers, new ArrayList<>(
+                mFeatures));
+    }
+
+    static class PendingArea {
+        final int mMinLayer;
+        final ArrayList<PendingArea> mChildren = new ArrayList<>();
+        final Feature mFeature;
+        final PendingArea mParent;
+        int mMaxLayer;
+        DisplayArea mExisting;
+
+        PendingArea(Feature feature,
+                int minLayer,
+                PendingArea parent) {
+            mMinLayer = minLayer;
+            mFeature = feature;
+            mParent = parent;
+        }
+
+        int computeMaxLayer() {
+            for (int i = 0; i < mChildren.size(); i++) {
+                mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer());
+            }
+            return mMaxLayer;
+        }
+
+        void instantiateChildren(DisplayArea<DisplayArea> parent,
+                DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<?
+                extends WindowContainer>>> areas) {
+            mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
+            for (int i = 0; i < mChildren.size(); i++) {
+                final PendingArea child = mChildren.get(i);
+                final DisplayArea area = child.createArea(parent, areaForLayer);
+                parent.addChild(area, WindowContainer.POSITION_TOP);
+                if (mFeature != null) {
+                    areas.get(mFeature).add(area);
+                }
+                child.instantiateChildren(area, areaForLayer, level + 1, areas);
+            }
+        }
+
+        private DisplayArea createArea(DisplayArea<DisplayArea> parent,
+                DisplayArea.Tokens[] areaForLayer) {
+            if (mExisting != null) {
+                return mExisting;
+            }
+            DisplayArea.Type type;
+            if (mMinLayer > APPLICATION_LAYER) {
+                type = DisplayArea.Type.ABOVE_TASKS;
+            } else if (mMaxLayer < APPLICATION_LAYER) {
+                type = DisplayArea.Type.BELOW_TASKS;
+            } else {
+                type = DisplayArea.Type.ANY;
+            }
+            if (mFeature == null) {
+                final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
+                        "Leaf:" + mMinLayer + ":" + mMaxLayer);
+                for (int i = mMinLayer; i <= mMaxLayer; i++) {
+                    areaForLayer[i] = leaf;
+                }
+                return leaf;
+            } else {
+                return new DisplayArea(parent.mWmService, type, mFeature.mName + ":"
+                        + mMinLayer + ":" + mMaxLayer);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8f9caea..2bb6703 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7317,9 +7317,11 @@
                 Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
                         + " imeTargetWindowToken=" + imeTargetWindowToken);
             }
-            final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
-            if (imeTarget != null) {
-                imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+            synchronized (mGlobalLock) {
+                final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+                if (imeTarget != null) {
+                    imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+                }
             }
         }
 
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 7e6f79f..70a9c09 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -18,18 +18,27 @@
 #define LOG_TAG "PackageManagerShellCommandDataLoader-jni"
 #include <android-base/logging.h>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <nativehelper/JNIHelp.h>
-#include "android-base/file.h"
+#include <cutils/trace.h>
+#include <sys/eventfd.h>
+#include <sys/poll.h>
 
-#include <endian.h>
+#include <nativehelper/JNIHelp.h>
 
 #include <core_jni_helpers.h>
+#include <endian.h>
 
 #include "dataloader.h"
 
+#include <charconv>
 #include <chrono>
+#include <span>
+#include <string>
 #include <thread>
+#include <unordered_map>
+#include <unordered_set>
 
 namespace android {
 
@@ -39,9 +48,26 @@
 using android::base::ReadFully;
 using android::base::unique_fd;
 
+using namespace std::literals;
+
+using BlockSize = int16_t;
+using FileIdx = int16_t;
+using BlockIdx = int32_t;
+using NumBlocks = int32_t;
+using CompressionType = int16_t;
+using RequestType = int16_t;
+using MagicType = uint32_t;
+
 static constexpr int BUFFER_SIZE = 256 * 1024;
 static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE;
 
+static constexpr int COMMAND_SIZE = 4 + 2 + 2 + 4; // bytes
+static constexpr int HEADER_SIZE = 2 + 2 + 4 + 2;  // bytes
+static constexpr std::string_view OKAY = "OKAY"sv;
+static constexpr MagicType INCR = 0x52434e49; // BE INCR
+
+static constexpr auto PollTimeoutMs = 5000;
+
 struct JniIds {
     jclass packageManagerShellCommandDataLoader;
     jmethodID pmscdLookupShellCommand;
@@ -85,6 +111,70 @@
     return ids;
 }
 
+struct BlockHeader {
+    FileIdx fileIdx = -1;
+    CompressionType compressionType = -1;
+    BlockIdx blockIdx = -1;
+    BlockSize blockSize = -1;
+} __attribute__((packed));
+
+static_assert(sizeof(BlockHeader) == HEADER_SIZE);
+
+static constexpr RequestType EXIT = 0;
+static constexpr RequestType BLOCK_MISSING = 1;
+static constexpr RequestType PREFETCH = 2;
+
+struct RequestCommand {
+    MagicType magic;
+    RequestType requestType;
+    FileIdx fileIdx;
+    BlockIdx blockIdx;
+} __attribute__((packed));
+
+static_assert(COMMAND_SIZE == sizeof(RequestCommand));
+
+static bool sendRequest(int fd, RequestType requestType, FileIdx fileIdx = -1,
+                        BlockIdx blockIdx = -1) {
+    const RequestCommand command{.magic = INCR,
+                                 .requestType = static_cast<int16_t>(be16toh(requestType)),
+                                 .fileIdx = static_cast<int16_t>(be16toh(fileIdx)),
+                                 .blockIdx = static_cast<int32_t>(be32toh(blockIdx))};
+    return android::base::WriteFully(fd, &command, sizeof(command));
+}
+
+static int waitForDataOrSignal(int fd, int event_fd) {
+    struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}};
+    // Wait indefinitely until either data is ready or stop signal is received
+    int res = poll(pfds, 2, PollTimeoutMs);
+    if (res <= 0) {
+        return res;
+    }
+    // First check if there is a stop signal
+    if (pfds[1].revents == POLLIN) {
+        return event_fd;
+    }
+    // Otherwise check if incoming data is ready
+    if (pfds[0].revents == POLLIN) {
+        return fd;
+    }
+    return -1;
+}
+
+static bool readChunk(int fd, std::vector<uint8_t>& data) {
+    int32_t size;
+    if (!android::base::ReadFully(fd, &size, sizeof(size))) {
+        return false;
+    }
+    size = int32_t(be32toh(size));
+    if (size <= 0) {
+        return false;
+    }
+    data.resize(size);
+    return android::base::ReadFully(fd, data.data(), data.size());
+}
+
+BlockHeader readHeader(std::span<uint8_t>& data);
+
 static inline int32_t readBEInt32(borrowed_fd fd) {
     int32_t result;
     ReadFully(fd, &result, sizeof(result));
@@ -106,6 +196,22 @@
     return readBEInt32(fd); // size of the verity tree
 }
 
+static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) {
+    constexpr int SHA256_DIGEST_SIZE = 32;
+    constexpr int digest_size = SHA256_DIGEST_SIZE;
+    constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
+
+    IncFsSize total_tree_block_count = 0;
+
+    auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
+    auto hash_block_count = block_count;
+    for (auto i = 0; hash_block_count > 1; i++) {
+        hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+        total_tree_block_count += hash_block_count;
+    }
+    return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
+}
+
 static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) {
     if (!pfd) {
         ALOGE("Missing In ParcelFileDescriptor.");
@@ -125,8 +231,9 @@
 struct InputDesc {
     unique_fd fd;
     IncFsSize size;
-    IncFsBlockKind kind;
-    bool waitOnEof;
+    IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA;
+    bool waitOnEof = false;
+    bool streaming = false;
 };
 using InputDescs = std::vector<InputDesc>;
 
@@ -135,8 +242,7 @@
     InputDescs result;
     result.reserve(2);
 
-    const bool fromStdin = (metadata.size == 0 || *metadata.data == '-');
-    if (fromStdin) {
+    if (metadata.size == 0 || *metadata.data == '-') {
         // stdin
         auto fd = convertPfdToFdAndDup(
                 env, jni,
@@ -146,12 +252,29 @@
             result.push_back(InputDesc{
                     .fd = std::move(fd),
                     .size = size,
-                    .kind = INCFS_BLOCK_KIND_DATA,
                     .waitOnEof = true,
             });
         }
         return result;
     }
+    if (*metadata.data == '+') {
+        // verity tree from stdin, rest is streaming
+        auto fd = convertPfdToFdAndDup(
+                env, jni,
+                env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                            jni.pmscdGetStdInPFD, shellCommand));
+        if (fd.ok()) {
+            auto treeSize = verityTreeSizeForFile(size);
+            result.push_back(InputDesc{
+                    .fd = std::move(fd),
+                    .size = treeSize,
+                    .kind = INCFS_BLOCK_KIND_HASH,
+                    .waitOnEof = true,
+                    .streaming = true,
+            });
+        }
+        return result;
+    }
 
     // local file and possibly signature
     const std::string filePath(metadata.data, metadata.size);
@@ -163,13 +286,17 @@
                                         jni.pmscdGetLocalFile, shellCommand,
                                         env->NewStringUTF(idsigPath.c_str())));
     if (idsigFd.ok()) {
-        ALOGE("idsig found, skipping to the tree");
-        auto treeSize = skipIdSigHeaders(idsigFd);
+        auto treeSize = verityTreeSizeForFile(size);
+        auto actualTreeSize = skipIdSigHeaders(idsigFd);
+        if (treeSize != actualTreeSize) {
+            ALOGE("Verity tree size mismatch: %d vs .idsig: %d.", int(treeSize),
+                  int(actualTreeSize));
+            return {};
+        }
         result.push_back(InputDesc{
                 .fd = std::move(idsigFd),
                 .size = treeSize,
                 .kind = INCFS_BLOCK_KIND_HASH,
-                .waitOnEof = false,
         });
     }
 
@@ -182,8 +309,6 @@
         result.push_back(InputDesc{
                 .fd = std::move(fileFd),
                 .size = size,
-                .kind = INCFS_BLOCK_KIND_DATA,
-                .waitOnEof = false,
         });
     }
 
@@ -226,19 +351,32 @@
                   android::dataloader::StatusListenerPtr statusListener,
                   android::dataloader::ServiceConnectorPtr,
                   android::dataloader::ServiceParamsPtr) final {
+        CHECK(ifs) << "ifs can't be null";
+        CHECK(statusListener) << "statusListener can't be null";
         mArgs = params.arguments();
         mIfs = ifs;
+        mStatusListener = statusListener;
         return true;
     }
     bool onStart() final { return true; }
-    void onStop() final {}
-    void onDestroy() final {}
+    void onStop() final {
+        mStopReceiving = true;
+        eventfd_write(mEventFd, 1);
+        if (mReceiverThread.joinable()) {
+            mReceiverThread.join();
+        }
+    }
+    void onDestroy() final {
+        ALOGE("Sending EXIT to server.");
+        sendRequest(mOutFd, EXIT);
+        // Make sure the receiver thread stopped.
+        CHECK(!mReceiverThread.joinable());
 
-    // IFS callbacks.
-    void onPendingReads(const dataloader::PendingReads& pendingReads) final {}
-    void onPageReads(const dataloader::PageReads& pageReads) final {}
+        mInFd.reset();
+        mOutFd.reset();
+    }
 
-    // FS callbacks.
+    // Installation.
     bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final {
         JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
         const auto& jni = jniIds(env);
@@ -257,6 +395,7 @@
         std::vector<IncFsDataBlock> blocks;
         blocks.reserve(BLOCKS_COUNT);
 
+        unique_fd streamingFd;
         for (auto&& file : addedFiles) {
             auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata);
             if (inputs.empty()) {
@@ -267,7 +406,6 @@
             }
 
             const auto fileId = IncFs_FileIdFromMetadata(file.metadata);
-
             const auto incfsFd(mIfs->openWrite(fileId));
             if (incfsFd < 0) {
                 ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
@@ -277,6 +415,9 @@
             }
 
             for (auto&& input : inputs) {
+                if (input.streaming && !streamingFd.ok()) {
+                    streamingFd.reset(dup(input.fd));
+                }
                 if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof,
                                  &buffer, &blocks)) {
                     ALOGE("Failed to copy data to IncFS file for metadata: %.*s, final file name "
@@ -288,7 +429,12 @@
             }
         }
 
-        ALOGE("All done.");
+        if (streamingFd.ok()) {
+            ALOGE("onPrepareImage: done, proceeding to streaming.");
+            return initStreaming(std::move(streamingFd));
+        }
+
+        ALOGE("onPrepareImage: done.");
         return true;
     }
 
@@ -378,11 +524,253 @@
         return true;
     }
 
+    // Read tracing.
+    struct TracedRead {
+        uint64_t timestampUs;
+        android::dataloader::FileId fileId;
+        uint32_t firstBlockIdx;
+        uint32_t count;
+    };
+
+    void onPageReads(const android::dataloader::PageReads& pageReads) final {
+        auto trace = atrace_is_tag_enabled(ATRACE_TAG);
+        if (CC_LIKELY(!trace)) {
+            return;
+        }
+
+        TracedRead last = {};
+        for (auto&& read : pageReads) {
+            if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
+                traceRead(last);
+                last = TracedRead{
+                        .timestampUs = read.bootClockTsUs,
+                        .fileId = read.id,
+                        .firstBlockIdx = (uint32_t)read.block,
+                        .count = 1,
+                };
+            } else {
+                ++last.count;
+            }
+        }
+        traceRead(last);
+    }
+
+    void traceRead(const TracedRead& read) {
+        if (!read.count) {
+            return;
+        }
+
+        FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
+        auto str = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
+                                               static_cast<long long>(read.firstBlockIdx),
+                                               static_cast<long long>(read.count),
+                                               static_cast<int>(fileIdx));
+        ATRACE_BEGIN(str.c_str());
+        ATRACE_END();
+    }
+
+    // Streaming.
+    bool initStreaming(unique_fd inout) {
+        mInFd.reset(dup(inout));
+        mOutFd.reset(dup(inout));
+        if (mInFd < 0 || mOutFd < 0) {
+            ALOGE("Failed to dup FDs.");
+            return false;
+        }
+
+        mEventFd.reset(eventfd(0, EFD_CLOEXEC));
+        if (mEventFd < 0) {
+            ALOGE("Failed to create eventfd.");
+            return false;
+        }
+
+        // Awaiting adb handshake.
+        char okay_buf[OKAY.size()];
+        if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
+            ALOGE("Failed to receive OKAY. Abort.");
+            return false;
+        }
+        if (std::string_view(okay_buf, OKAY.size()) != OKAY) {
+            ALOGE("Received '%.*s', expecting '%.*s'", (int)OKAY.size(), okay_buf, (int)OKAY.size(),
+                  OKAY.data());
+            return false;
+        }
+
+        mReceiverThread = std::thread([this]() { receiver(); });
+        ALOGI("Started streaming...");
+        return true;
+    }
+
+    // IFS callbacks.
+    void onPendingReads(const dataloader::PendingReads& pendingReads) final {
+        CHECK(mIfs);
+        for (auto&& pendingRead : pendingReads) {
+            const android::dataloader::FileId& fileId = pendingRead.id;
+            const auto blockIdx = static_cast<BlockIdx>(pendingRead.block);
+            /*
+            ALOGI("Missing: %d", (int) blockIdx);
+            */
+            FileIdx fileIdx = convertFileIdToFileIndex(fileId);
+            if (fileIdx < 0) {
+                ALOGE("Failed to handle event for fileid=%s. Ignore.",
+                      android::incfs::toString(fileId).c_str());
+                continue;
+            }
+            if (mRequestedFiles.insert(fileIdx).second) {
+                if (!sendRequest(mOutFd, PREFETCH, fileIdx, blockIdx)) {
+                    ALOGE("Failed to request prefetch for fileid=%s. Ignore.",
+                          android::incfs::toString(fileId).c_str());
+                    mRequestedFiles.erase(fileIdx);
+                    mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+                }
+            }
+            sendRequest(mOutFd, BLOCK_MISSING, fileIdx, blockIdx);
+        }
+    }
+
+    void receiver() {
+        std::vector<uint8_t> data;
+        std::vector<IncFsDataBlock> instructions;
+        std::unordered_map<FileIdx, unique_fd> writeFds;
+        while (!mStopReceiving) {
+            const int res = waitForDataOrSignal(mInFd, mEventFd);
+            if (res == 0) {
+                continue;
+            }
+            if (res < 0) {
+                ALOGE("Failed to poll. Abort.");
+                mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+                break;
+            }
+            if (res == mEventFd) {
+                ALOGE("Received stop signal. Exit.");
+                break;
+            }
+            if (!readChunk(mInFd, data)) {
+                ALOGE("Failed to read a message. Abort.");
+                mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+                break;
+            }
+            auto remainingData = std::span(data);
+            while (!remainingData.empty()) {
+                auto header = readHeader(remainingData);
+                if (header.fileIdx == -1 && header.compressionType == 0 && header.blockIdx == 0 &&
+                    header.blockSize == 0) {
+                    ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
+                          int(remainingData.size()));
+
+                    sendRequest(mOutFd, EXIT);
+                    mStopReceiving = true;
+                    break;
+                }
+                if (header.fileIdx < 0 || header.blockSize <= 0 || header.compressionType < 0 ||
+                    header.blockIdx < 0) {
+                    ALOGE("invalid header received. Abort.");
+                    mStopReceiving = true;
+                    break;
+                }
+                const FileIdx fileIdx = header.fileIdx;
+                const android::dataloader::FileId fileId = convertFileIndexToFileId(fileIdx);
+                if (!android::incfs::isValidFileId(fileId)) {
+                    ALOGE("Unknown data destination for file ID %d. "
+                          "Ignore.",
+                          header.fileIdx);
+                    continue;
+                }
+
+                auto& writeFd = writeFds[fileIdx];
+                if (writeFd < 0) {
+                    writeFd = this->mIfs->openWrite(fileId);
+                    if (writeFd < 0) {
+                        ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileIdx,
+                              -writeFd);
+                        break;
+                    }
+                }
+
+                const auto inst = IncFsDataBlock{
+                        .fileFd = writeFd,
+                        .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx),
+                        .compression = static_cast<IncFsCompressionKind>(header.compressionType),
+                        .kind = INCFS_BLOCK_KIND_DATA,
+                        .dataSize = static_cast<uint16_t>(header.blockSize),
+                        .data = (const char*)remainingData.data(),
+                };
+                instructions.push_back(inst);
+                remainingData = remainingData.subspan(header.blockSize);
+            }
+            writeInstructions(instructions);
+        }
+        writeInstructions(instructions);
+    }
+
+    void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
+        auto res = this->mIfs->writeBlocks(instructions);
+        if (res != instructions.size()) {
+            ALOGE("Dailed to write data to Incfs (res=%d when expecting %d)", res,
+                  int(instructions.size()));
+        }
+        instructions.clear();
+    }
+
+    FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) {
+        // FileId is a string in format '+FileIdx\0'.
+        const char* meta = (const char*)&fileId;
+        if (*meta != '+') {
+            return -1;
+        }
+
+        int fileIdx;
+        auto res = std::from_chars(meta + 1, meta + sizeof(fileId), fileIdx);
+        if (res.ec != std::errc{} || fileIdx < std::numeric_limits<FileIdx>::min() ||
+            fileIdx > std::numeric_limits<FileIdx>::max()) {
+            return -1;
+        }
+
+        return FileIdx(fileIdx);
+    }
+
+    android::dataloader::FileId convertFileIndexToFileId(FileIdx fileIdx) {
+        IncFsFileId fileId = {};
+        char* meta = (char*)&fileId;
+        *meta = '+';
+        if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx);
+            ec != std::errc()) {
+            return {};
+        }
+        return fileId;
+    }
+
     JavaVM* const mJvm;
     std::string mArgs;
-    android::dataloader::FilesystemConnectorPtr mIfs;
+    android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
+    android::dataloader::StatusListenerPtr mStatusListener = nullptr;
+    android::base::unique_fd mInFd;
+    android::base::unique_fd mOutFd;
+    android::base::unique_fd mEventFd;
+    std::thread mReceiverThread;
+    std::atomic<bool> mStopReceiving = false;
+    /** Tracks which files have been requested */
+    std::unordered_set<FileIdx> mRequestedFiles;
 };
 
+BlockHeader readHeader(std::span<uint8_t>& data) {
+    BlockHeader header;
+    if (data.size() < sizeof(header)) {
+        return header;
+    }
+
+    header.fileIdx = static_cast<FileIdx>(be16toh(*reinterpret_cast<const uint16_t*>(&data[0])));
+    header.compressionType =
+            static_cast<CompressionType>(be16toh(*reinterpret_cast<const uint16_t*>(&data[2])));
+    header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<const uint32_t*>(&data[4])));
+    header.blockSize =
+            static_cast<BlockSize>(be16toh(*reinterpret_cast<const uint16_t*>(&data[8])));
+    data = data.subspan(sizeof(header));
+
+    return header;
+}
+
 static void nativeInitialize(JNIEnv* env, jclass klass) {
     jniIds(env);
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e469067..aa5dafc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -57,6 +57,7 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
@@ -522,7 +523,8 @@
 
     /** Keyguard features that are allowed to be set on a managed profile */
     private static final int PROFILE_KEYGUARD_FEATURES =
-            PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;
+            NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER
+                    | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;
 
     private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
 
@@ -8168,16 +8170,20 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
-        if (isManagedProfile(userHandle)) {
-            if (parent) {
-                which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
-            } else {
-                which = which & PROFILE_KEYGUARD_FEATURES;
-            }
-        }
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
+            if (isManagedProfile(userHandle)) {
+                if (parent) {
+                    if (isProfileOwnerOfOrganizationOwnedDevice(ap)) {
+                        which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+                    } else {
+                        which = which & NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+                    }
+                } else {
+                    which = which & PROFILE_KEYGUARD_FEATURES;
+                }
+            }
             if (ap.disabledKeyguardFeatures != which) {
                 ap.disabledKeyguardFeatures = which;
                 saveSettingsLocked(userHandle);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index c8673f8..a904b42 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -322,7 +322,8 @@
     private void updateDefaultDialer(@NonNull UserData userData) {
         TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
         String defaultDialer = telecomManager != null
-                ? telecomManager.getDefaultDialerPackage(userData.getUserId()) : null;
+                ? telecomManager.getDefaultDialerPackage(
+                        new UserHandle(userData.getUserId())) : null;
         userData.setDefaultDialer(defaultDialer);
     }
 
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index 644c155..fba4f4e 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -59,7 +59,7 @@
      */
     boolean querySince(long sinceTime) {
         UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
-                mUserId, sinceTime, System.currentTimeMillis(), false, false, false);
+                mUserId, sinceTime, System.currentTimeMillis(), UsageEvents.SHOW_ALL_EVENT_DATA);
         if (usageEvents == null) {
             return false;
         }
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk
new file mode 100644
index 0000000..9161d86
--- /dev/null
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index f2f8ad1..8f70cca 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2188,6 +2188,42 @@
         assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
     }
 
+    public void testSetKeyguardDisabledFeaturesWithDO() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+
+        dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+        assertThat(dpm.getKeyguardDisabledFeatures(admin1)).isEqualTo(
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+    }
+
+    public void testSetKeyguardDisabledFeaturesWithPO() throws Exception {
+        setupProfileOwner();
+
+        dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+
+        assertThat(dpm.getKeyguardDisabledFeatures(admin1)).isEqualTo(
+                DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+    }
+
+    public void testSetKeyguardDisabledFeaturesWithPOOfOrganizationOwnedDevice()
+            throws Exception {
+        final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
+        final int MANAGED_PROFILE_ADMIN_UID =
+                UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        parentDpm.setKeyguardDisabledFeatures(admin1,
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+        assertThat(parentDpm.getKeyguardDisabledFeatures(admin1)).isEqualTo(
+                DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+    }
+
     public void testSetApplicationHiddenWithDO() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java
new file mode 100644
index 0000000..607cd81
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Locale;
+
+/**
+ * Tests for static methods of {@link HdmiControlService} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceStaticTest {
+
+    @Test
+    public void localToMenuLanguage_english() {
+        assertThat(HdmiControlService.localeToMenuLanguage(Locale.ENGLISH)).isEqualTo("eng");
+    }
+
+    @Test
+    public void localToMenuLanguage_german() {
+        assertThat(HdmiControlService.localeToMenuLanguage(Locale.GERMAN)).isEqualTo("ger");
+    }
+
+    @Test
+    public void localToMenuLanguage_taiwan() {
+        assertThat(HdmiControlService.localeToMenuLanguage(Locale.TAIWAN)).isEqualTo("chi");
+    }
+
+    @Test
+    public void localToMenuLanguage_macau() {
+        assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "MO"))).isEqualTo(
+                "chi");
+    }
+
+    @Test
+    public void localToMenuLanguage_hongkong() {
+        assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "HK"))).isEqualTo(
+                "chi");
+    }
+
+    @Test
+    public void localToMenuLanguage_chinese() {
+        assertThat(HdmiControlService.localeToMenuLanguage(Locale.CHINESE)).isEqualTo("zho");
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 8dae48c..0d4c6e8 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -19,6 +19,7 @@
 import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
 import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
 import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
+import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID;
@@ -39,6 +40,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -65,6 +68,7 @@
 import com.android.server.integrity.model.IntegrityCheckResult;
 import com.android.server.testutils.TestUtils;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -76,8 +80,9 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -87,6 +92,9 @@
     private static final String TEST_APP_PATH =
             "/data/local/tmp/AppIntegrityManagerServiceTestApp.apk";
 
+    private static final String TEST_APP_TWO_CERT_PATH =
+            "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
+
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
     private static final String VERSION = "version";
     private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
@@ -105,6 +113,11 @@
     private static final String INSTALLER_SHA256 =
             "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
 
+    private static final String DUMMY_APP_TWO_CERTS_CERT_1 =
+            "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94";
+    private static final String DUMMY_APP_TWO_CERTS_CERT_2 =
+            "94366E0A80F3A3F0D8171A15760B88E228CD6E1101F0414C98878724FBE70147";
+
     private static final String PLAY_STORE_PKG = "com.android.vending";
     private static final String ADB_INSTALLER = "adb";
     private static final String PLAY_STORE_CERT = "play_store_cert";
@@ -128,6 +141,7 @@
 
     private PackageManager mSpyPackageManager;
     private File mTestApk;
+    private File mTestApkTwoCerts;
 
     private final Context mRealContext = InstrumentationRegistry.getTargetContext();
     // under test
@@ -136,6 +150,10 @@
     @Before
     public void setup() throws Exception {
         mTestApk = new File(TEST_APP_PATH);
+        mTestApkTwoCerts = File.createTempFile("AppIntegrity", ".apk");
+        try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
+            Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
+        }
 
         mService =
                 new AppIntegrityManagerServiceImpl(
@@ -154,6 +172,11 @@
         when(mIntegrityFileManager.initialized()).thenReturn(true);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        mTestApkTwoCerts.delete();
+    }
+
     @Test
     public void updateRuleSet_notAuthorized() throws Exception {
         makeUsSystemApp();
@@ -268,20 +291,16 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
+        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
 
         ArgumentCaptor<AppInstallMetadata> metadataCaptor =
                 ArgumentCaptor.forClass(AppInstallMetadata.class);
-        Map<String, String> allowedInstallers = new HashMap<>();
-        ArgumentCaptor<Map<String, String>> allowedInstallersCaptor =
-                ArgumentCaptor.forClass(allowedInstallers.getClass());
         verify(mRuleEvaluationEngine)
-                .evaluate(metadataCaptor.capture(), allowedInstallersCaptor.capture());
+                .evaluate(metadataCaptor.capture());
         AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
-        allowedInstallers = allowedInstallersCaptor.getValue();
         assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
         assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
         assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
@@ -289,9 +308,34 @@
         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
         assertFalse(appInstallMetadata.isPreInstalled());
         // These are hardcoded in the test apk android manifest
+        Map<String, String> allowedInstallers =
+                appInstallMetadata.getAllowedInstallersAndCertificates();
         assertEquals(2, allowedInstallers.size());
         assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG));
-        assertEquals(ADB_CERT, allowedInstallers.get(ADB_INSTALLER));
+        assertEquals(INSTALLER_CERTIFICATE_NOT_EVALUATED, allowedInstallers.get(ADB_INSTALLER));
+    }
+
+    @Test
+    public void handleBroadcast_correctArgs_multipleCerts() throws Exception {
+        whitelistUsAsRuleProvider();
+        makeUsSystemApp();
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mMockContext)
+                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+        Intent intent = makeVerificationIntent();
+        intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE);
+        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
+
+        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+        runJobInHandler();
+
+        ArgumentCaptor<AppInstallMetadata> metadataCaptor =
+                ArgumentCaptor.forClass(AppInstallMetadata.class);
+        verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
+        AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
+        assertThat(appInstallMetadata.getAppCertificates()).containsExactly(
+                DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2);
     }
 
     @Test
@@ -303,7 +347,7 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
+        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
@@ -321,7 +365,7 @@
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        when(mRuleEvaluationEngine.evaluate(any(), any()))
+        when(mRuleEvaluationEngine.evaluate(any()))
                 .thenReturn(
                         IntegrityCheckResult.deny(
                                 Arrays.asList(
@@ -349,7 +393,7 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
+        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
@@ -377,7 +421,7 @@
         verify(mMockContext, atLeastOnce())
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
-        when(mRuleEvaluationEngine.evaluate(any(), any()))
+        when(mRuleEvaluationEngine.evaluate(any()))
                 .thenReturn(IntegrityCheckResult.deny(/* rule= */ null));
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
index b0b9596..0488745 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.Mockito.when;
 
 import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.IntegrityFormula;
+import android.content.integrity.Rule;
 
 import com.android.server.integrity.IntegrityFileManager;
 import com.android.server.integrity.model.IntegrityCheckResult;
@@ -33,7 +35,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -60,13 +61,14 @@
 
         mEngine = new RuleEvaluationEngine(mIntegrityFileManager);
 
-        when(mIntegrityFileManager.readRules(any())).thenReturn(new ArrayList<>());
+        when(mIntegrityFileManager.readRules(any())).thenReturn(Collections.singletonList(new Rule(
+                IntegrityFormula.Installer.notAllowedByManifest(), Rule.DENY)));
+
+        when(mIntegrityFileManager.initialized()).thenReturn(true);
     }
 
     @Test
     public void testAllowedInstallers_empty() {
-        Map<String, String> allowedInstallers = Collections.emptyMap();
-
         AppInstallMetadata appInstallMetadata1 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
@@ -83,11 +85,11 @@
                         .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
                         .build();
 
-        assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-        assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-        assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
     }
 
@@ -100,32 +102,36 @@
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
                         .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
 
         AppInstallMetadata appInstallMetadata2 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(RANDOM_INSTALLER)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
 
         AppInstallMetadata appInstallMetadata3 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
 
         AppInstallMetadata appInstallMetadata4 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata4).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
     }
 
@@ -138,57 +144,37 @@
         AppInstallMetadata appInstallMetadata1 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
 
         AppInstallMetadata appInstallMetadata2 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_2)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
 
         AppInstallMetadata appInstallMetadata3 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
 
         AppInstallMetadata appInstallMetadata4 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_2)
+                        .setAllowedInstallersAndCert(allowedInstallers)
                         .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
-        assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.DENY);
-    }
-
-    @Test
-    public void manifestBasedRuleEvaluationWorksEvenWhenIntegrityFilesAreUnavailable() {
-        when(mIntegrityFileManager.initialized()).thenReturn(false);
-
-        Map<String, String> allowedInstallers =
-                Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT);
-
-        AppInstallMetadata appInstallMetadata1 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
-        AppInstallMetadata appInstallMetadata2 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(RANDOM_INSTALLER)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+        assertThat(mEngine.evaluate(appInstallMetadata4).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 498d888..6769faa 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -142,7 +142,8 @@
         when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
         when(mContext.getSystemServiceName(TelecomManager.class)).thenReturn(
                 Context.TELECOM_SERVICE);
-        when(mTelecomManager.getDefaultDialerPackage(anyInt())).thenReturn(TEST_PKG_NAME);
+        when(mTelecomManager.getDefaultDialerPackage(any(UserHandle.class)))
+                .thenReturn(TEST_PKG_NAME);
 
         when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
                 TimeUnit.class))).thenReturn(mScheduledFuture);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index 01d9dc0..c8543c4 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -19,9 +19,9 @@
 import static org.junit.Assert.assertEquals;
 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.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -189,7 +189,7 @@
     private void addUsageEvents(UsageEvents.Event... events) {
         UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{});
         when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
-                anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(usageEvents);
+                eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents);
     }
 
     private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index e768205..9e57763 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -145,6 +145,7 @@
     static class MyInjector extends AppStandbyController.Injector {
         long mElapsedRealtime;
         boolean mIsAppIdleEnabled = true;
+        boolean mIsCharging;
         List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
         boolean mDisplayOn;
         DisplayManager.DisplayListener mDisplayListener;
@@ -179,6 +180,11 @@
         }
 
         @Override
+        boolean isCharging() {
+            return mIsCharging;
+        }
+
+        @Override
         boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
             return mPowerSaveWhitelistExceptIdle.contains(packageName);
         }
@@ -281,6 +287,13 @@
         } catch (PackageManager.NameNotFoundException nnfe) {}
     }
 
+    private void setChargingState(AppStandbyController controller, boolean charging) {
+        mInjector.mIsCharging = charging;
+        if (controller != null) {
+            controller.setChargingState(charging);
+        }
+    }
+
     private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
         mInjector.mIsAppIdleEnabled = enabled;
         if (controller != null) {
@@ -297,6 +310,7 @@
         controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mInjector.setDisplayOn(false);
         mInjector.setDisplayOn(true);
+        setChargingState(controller, false);
         controller.checkIdleStates(USER_ID);
         assertNotEquals(STANDBY_BUCKET_EXEMPTED,
                 controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
@@ -324,6 +338,46 @@
                         mInjector.mElapsedRealtime, false));
     }
 
+    @Test
+    public void testIsAppIdle_Charging() throws Exception {
+        setChargingState(mController, false);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+        setChargingState(mController, true);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+        assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+        setChargingState(mController, false);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+    }
+
+    @Test
+    public void testIsAppIdle_Enabled() throws Exception {
+        setChargingState(mController, false);
+        setAppIdleEnabled(mController, true);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+        setAppIdleEnabled(mController, false);
+        assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+        setAppIdleEnabled(mController, true);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+    }
+
     private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
         mInjector.mElapsedRealtime = elapsedTime;
         controller.checkIdleStates(USER_ID);
diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
index f5dbf43..98572d4 100644
--- a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
@@ -22,7 +22,7 @@
     <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="28" />
 
     <application android:hasCode="false">
-        <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb|"/>
+        <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb"/>
     </application>
 </manifest>
 
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 c19608b..bc33f08 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -51,8 +51,6 @@
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
@@ -1144,14 +1142,15 @@
     }
 
     @Test
-    public void testEnqueueNotificationWithTag_WritesExpectedLog() throws Exception {
+    public void testEnqueueNotificationWithTag_WritesExpectedLogs() throws Exception {
         final String tag = "testEnqueueNotificationWithTag_WritesExpectedLog";
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
         assertEquals(1, mNotificationRecordLogger.getCalls().size());
+
         NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
-        assertTrue(call.shouldLog);
+        assertTrue(call.shouldLogReported);
         assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
                 call.event);
         assertNotNull(call.r);
@@ -1161,7 +1160,6 @@
         assertEquals(PKG, call.r.getSbn().getPackageName());
         assertEquals(0, call.r.getSbn().getId());
         assertEquals(tag, call.r.getSbn().getTag());
-        assertNotNull(call.r.getSbn().getInstanceId());
         assertEquals(0, call.getInstanceId());  // Fake instance IDs are assigned in order
     }
 
@@ -1180,13 +1178,13 @@
         waitForIdle();
         assertEquals(2, mNotificationRecordLogger.getCalls().size());
 
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+        assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
                 mNotificationRecordLogger.get(0).event);
         assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
 
-        assertTrue(mNotificationRecordLogger.get(1).shouldLog);
+        assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
                 mNotificationRecordLogger.get(1).event);
@@ -1195,16 +1193,37 @@
     }
 
     @Test
-    public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates() throws Exception {
-        final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates";
+    public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate() throws Exception {
+        final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate";
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
                 generateNotificationRecord(null).getNotification(), 0);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
         assertEquals(2, mNotificationRecordLogger.getCalls().size());
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
-        assertFalse(mNotificationRecordLogger.get(1).shouldLog);
+        assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+        assertEquals(
+                NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+                mNotificationRecordLogger.get(0).event);
+        assertFalse(mNotificationRecordLogger.get(1).shouldLogReported);
+        assertNull(mNotificationRecordLogger.get(1).event);
+    }
+
+    @Test
+    public void testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate() throws Exception {
+        final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate";
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
+                generateNotificationRecord(null).getNotification(),
+                0);
+        final Notification notif = generateNotificationRecord(null).getNotification();
+        notif.extras.putString(Notification.EXTRA_TITLE, "Changed title");
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0);
+        waitForIdle();
+        assertEquals(2, mNotificationRecordLogger.getCalls().size());
+        assertEquals(
+                NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+                mNotificationRecordLogger.get(0).event);
+        assertNull(mNotificationRecordLogger.get(1).event);
     }
 
     @Test
@@ -1224,20 +1243,18 @@
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
                 mNotificationRecordLogger.get(0).event);
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+        assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
         assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
 
-        assertEquals(REASON_APP_CANCEL, mNotificationRecordLogger.get(1).reason);
         assertEquals(
                 NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
                 mNotificationRecordLogger.get(1).event);
-        assertTrue(mNotificationRecordLogger.get(1).shouldLog);
         assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
 
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
                 mNotificationRecordLogger.get(2).event);
-        assertTrue(mNotificationRecordLogger.get(2).shouldLog);
+        assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
         // New instance ID because notification was canceled before re-post
         assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId());
     }
@@ -3423,11 +3440,9 @@
         // so we only get the cancel notification.
         assertEquals(1, mNotificationRecordLogger.getCalls().size());
 
-        assertEquals(REASON_CANCEL, mNotificationRecordLogger.get(0).reason);
         assertEquals(
                 NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
                 mNotificationRecordLogger.get(0).event);
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
         assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
     }
 
@@ -4353,6 +4368,34 @@
     }
 
     @Test
+    public void testOnNotificationVisibilityChanged_triggersVisibilityLog() {
+        final NotificationRecord r = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        r.setTextChanged(true);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[]
+                {NotificationVisibility.obtain(r.getKey(), 1, 1, true)},
+                new NotificationVisibility[]{});
+
+        assertEquals(1, mNotificationRecordLogger.getCalls().size());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
+                mNotificationRecordLogger.get(0).event);
+        assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(
+                new NotificationVisibility[]{},
+                new NotificationVisibility[]
+                        {NotificationVisibility.obtain(r.getKey(), 1, 1, true)}
+        );
+
+        assertEquals(2, mNotificationRecordLogger.getCalls().size());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
+                mNotificationRecordLogger.get(1).event);
+        assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+    }
+
+    @Test
     public void testSetNotificationsShownFromListener_triggersInterruptionUsageStat()
             throws RemoteException {
         final NotificationRecord r = generateNotificationRecord(
@@ -5400,21 +5443,7 @@
     }
 
     @Test
-    public void testCancelAllNotifications_cancelsBubble() throws Exception {
-        final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
-        nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
-        mService.addNotification(nr);
-
-        mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId());
-        waitForIdle();
-
-        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
-        assertEquals(0, notifs.length);
-        assertEquals(0, mService.getNotificationRecordCount());
-    }
-
-    @Test
-    public void testAppCancelNotifications_cancelsBubbles() throws Exception {
+    public void testCancelNotificationsFromApp_cancelsBubbles() throws Exception {
         final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
         nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
 
@@ -5440,6 +5469,20 @@
     }
 
     @Test
+    public void testCancelAllNotificationsFromApp_cancelsBubble() throws Exception {
+        final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+        nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
+        mService.addNotification(nr);
+
+        mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId());
+        waitForIdle();
+
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+        assertEquals(0, notifs.length);
+        assertEquals(0, mService.getNotificationRecordCount());
+    }
+
+    @Test
     public void testCancelAllNotificationsFromListener_ignoresBubbles() throws Exception {
         final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
         final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
@@ -5475,6 +5518,25 @@
     }
 
     @Test
+    public void testCancelAllNotificationsFromStatusBar_ignoresBubble() throws Exception {
+        // GIVEN a notification bubble
+        final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+        nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
+        mService.addNotification(nr);
+
+        // WHEN the status bar clears all notifications
+        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+                nr.getSbn().getUserId());
+        waitForIdle();
+
+        // THEN the bubble notification does not get removed
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifs.length);
+        assertEquals(1, mService.getNotificationRecordCount());
+    }
+
+
+    @Test
     public void testGetAllowedAssistantAdjustments() throws Exception {
         List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null);
         assertNotNull(capabilities);
@@ -6132,6 +6194,26 @@
     }
 
     @Test
+    public void testNotificationBubbles_bubbleStays_whenClicked()
+            throws Exception {
+        // GIVEN a notification that has the auto cancels flag (cancel on click) and is a bubble
+        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+        final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+        nr.getSbn().getNotification().flags |= FLAG_BUBBLE | FLAG_AUTO_CANCEL;
+        mService.addNotification(nr);
+
+        // WHEN we click the notification
+        final NotificationVisibility nv = NotificationVisibility.obtain(nr.getKey(), 1, 2, true);
+        mService.mNotificationDelegate.onNotificationClick(mUid, Binder.getCallingPid(),
+                nr.getKey(), nv);
+        waitForIdle();
+
+        // THEN the bubble should still exist
+        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsAfter.length);
+    }
+
+    @Test
     public void testLoadDefaultApprovedServices_emptyResources() {
         TestableResources tr = mContext.getOrCreateTestableResources();
         tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, "");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index b120dbe..2a17bae 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -26,24 +26,26 @@
  */
 class NotificationRecordLoggerFake implements NotificationRecordLogger {
     static class CallRecord extends NotificationRecordPair {
-        static final int INVALID = -1;
-        public int position = INVALID, buzzBeepBlink = INVALID, reason = INVALID;
-        public boolean shouldLog;
         public UiEventLogger.UiEventEnum event;
+
+        // The following fields are only relevant to maybeLogNotificationPosted() calls.
+        static final int INVALID = -1;
+        public int position = INVALID, buzzBeepBlink = INVALID;
+        public boolean shouldLogReported;
+
         CallRecord(NotificationRecord r, NotificationRecord old, int position,
                 int buzzBeepBlink) {
             super(r, old);
-
             this.position = position;
             this.buzzBeepBlink = buzzBeepBlink;
-            shouldLog = shouldLog(buzzBeepBlink);
-            event = NotificationReportedEvent.fromRecordPair(this);
+            shouldLogReported = shouldLogReported(buzzBeepBlink);
+            event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null;
         }
-        CallRecord(NotificationRecord r, int reason, int dismissalSurface) {
+
+        CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
             super(r, null);
-            this.reason = reason;
-            shouldLog = true;
-            event = NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface);
+            shouldLogReported = false;
+            this.event = event;
         }
     }
     private List<CallRecord> mCalls = new ArrayList<>();
@@ -57,14 +59,19 @@
     }
 
     @Override
-    public void logNotificationReported(NotificationRecord r, NotificationRecord old,
+    public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
             int position, int buzzBeepBlink) {
         mCalls.add(new CallRecord(r, old, position, buzzBeepBlink));
     }
 
     @Override
     public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
-        mCalls.add(new CallRecord(r, reason, dismissalSurface));
+        mCalls.add(new CallRecord(r,
+                NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface)));
     }
 
+    @Override
+    public void logNotificationVisibility(NotificationRecord r, boolean visible) {
+        mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible)));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
new file mode 100644
index 0000000..8ac1d24
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.DisplayArea.Type.ANY;
+import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import static java.util.stream.Collectors.toList;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import org.hamcrest.CustomTypeSafeMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Presubmit
+public class DisplayAreaPolicyBuilderTest {
+
+    @Rule
+    public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
+
+    private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
+
+    @Test
+    public void testBuilder() {
+        WindowManagerService wms = mSystemServices.getWindowManagerService();
+        DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms);
+        DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
+        DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks");
+
+        final Feature foo;
+        final Feature bar;
+
+        DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+                .addFeature(foo = new Feature.Builder(mPolicy, "Foo")
+                        .upTo(TYPE_STATUS_BAR)
+                        .and(TYPE_NAVIGATION_BAR)
+                        .build())
+                .addFeature(bar = new Feature.Builder(mPolicy, "Bar")
+                        .all()
+                        .except(TYPE_STATUS_BAR)
+                        .build())
+                .build(wms, mock(DisplayContent.class), root, ime, tasks);
+
+        policy.attachDisplayAreas();
+
+        assertThat(policy.getDisplayAreas(foo), is(not(empty())));
+        assertThat(policy.getDisplayAreas(bar), is(not(empty())));
+
+        assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
+                is(decendantOfOneOf(policy.getDisplayAreas(foo))));
+        assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
+                is(not(decendantOfOneOf(policy.getDisplayAreas(bar)))));
+
+        assertThat(tasks,
+                is(decendantOfOneOf(policy.getDisplayAreas(foo))));
+        assertThat(tasks,
+                is(decendantOfOneOf(policy.getDisplayAreas(bar))));
+
+        assertThat(ime,
+                is(decendantOfOneOf(policy.getDisplayAreas(foo))));
+        assertThat(ime,
+                is(decendantOfOneOf(policy.getDisplayAreas(bar))));
+
+        List<DisplayArea<?>> actualOrder = collectLeafAreas(root);
+        Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks);
+        actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
+
+        Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
+                v -> v.stream().min(Integer::compareTo).get());
+        Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets,
+                v -> v.stream().max(Integer::compareTo).get());
+
+        assertThat(expectedByMinLayer, is(equalTo(expectedByMaxLayer)));
+        assertThat(actualOrder, is(equalTo(expectedByMaxLayer)));
+    }
+
+    private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) {
+        return zSets.entrySet().stream().collect(Collectors.toMap(
+                Map.Entry::getKey,
+                e -> f.apply(e.getValue())));
+    }
+
+    private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) {
+        ArrayList<DisplayArea<?>> leafs = new ArrayList<>();
+        traverseLeafAreas(root, leafs::add);
+        return leafs;
+    }
+
+    private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
+            DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root,
+            DisplayArea<WindowContainer> ime,
+            DisplayArea<ActivityStack> tasks) {
+        Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>();
+        int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION,
+                TYPE_APPLICATION_OVERLAY};
+        for (int type : types) {
+            WindowToken token = tokenOfType(type);
+            recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets);
+        }
+        recordLayer(tasks, APPLICATION_LAYER, zSets);
+        recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets);
+        return zSets;
+    }
+
+    private void recordLayer(DisplayArea<?> area, int layer,
+            Map<DisplayArea<?>,  Set<Integer>> zSets) {
+        zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer);
+    }
+
+    private Matcher<WindowContainer> decendantOfOneOf(List<? extends WindowContainer> expected) {
+        return new CustomTypeSafeMatcher<WindowContainer>("descendant of one of " + expected) {
+            @Override
+            protected boolean matchesSafely(WindowContainer actual) {
+                for (WindowContainer expected : expected) {
+                    WindowContainer candidate = actual;
+                    while (candidate != null && candidate.getParent() != candidate) {
+                        if (candidate.getParent() == expected) {
+                            return true;
+                        }
+                        candidate = candidate.getParent();
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            protected void describeMismatchSafely(WindowContainer item,
+                    Description description) {
+                description.appendText("was ").appendValue(item);
+                while (item != null && item.getParent() != item) {
+                    item = item.getParent();
+                    description.appendText(", child of ").appendValue(item);
+                }
+            }
+        };
+    }
+
+    private WindowToken tokenOfType(int type) {
+        WindowToken m = mock(WindowToken.class);
+        when(m.getWindowLayerFromType()).thenReturn(mPolicy.getWindowLayerFromTypeLw(type));
+        return m;
+    }
+
+    private static void traverseLeafAreas(DisplayArea<?> root, Consumer<DisplayArea<?>> consumer) {
+        boolean leaf = true;
+        for (int i = 0; i < root.getChildCount(); i++) {
+            WindowContainer child = root.getChildAt(i);
+            if (child instanceof DisplayArea<?>) {
+                traverseLeafAreas((DisplayArea<?>) child, consumer);
+                leaf = false;
+            }
+        }
+        if (leaf) {
+            consumer.accept(root);
+        }
+    }
+
+    private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root {
+
+        SurfacelessDisplayAreaRoot(WindowManagerService wms) {
+            super(wms);
+        }
+
+        @Override
+        SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+            return new MockSurfaceControlBuilder();
+        }
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index c1a1d5e..3120631 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -34,13 +34,13 @@
     @Test
     public void testFromResources_emptyProvider() {
         Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")),
-                Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+                Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class));
     }
 
     @Test
     public void testFromResources_nullProvider() {
         Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)),
-                Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+                Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class));
     }
 
     @Test
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 f517881..8ad7505 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -56,6 +56,7 @@
 import android.os.PowerSaveState;
 import android.os.StrictMode;
 import android.os.UserHandle;
+import android.util.Log;
 import android.view.InputChannel;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -120,11 +121,22 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
+                Throwable throwable = null;
                 try {
                     runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp);
                     base.evaluate();
+                } catch (Throwable t) {
+                    throwable = t;
                 } finally {
-                    tearDown();
+                    try {
+                        tearDown();
+                    } catch (Throwable t) {
+                        if (throwable != null) {
+                            Log.e("SystemServicesTestRule", "Suppressed: ", throwable);
+                            t.addSuppressed(throwable);
+                        }
+                        throw t;
+                    }
                 }
             }
         };
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 420695d..df5b311 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -500,6 +500,19 @@
                 == PackageManager.PERMISSION_GRANTED);
     }
 
+    /**
+     * Obfuscate both {@link UsageEvents.Event#NOTIFICATION_SEEN} and
+     * {@link UsageEvents.Event#NOTIFICATION_INTERRUPTION} events if the provided calling uid does
+     * not hold the {@link android.Manifest.permission.MANAGE_NOTIFICATIONS} permission.
+     */
+    private boolean shouldObfuscateNotificationEvents(int callingPid, int callingUid) {
+        if (callingUid == Process.SYSTEM_UID) {
+            return false;
+        }
+        return !(getContext().checkPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS,
+                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED);
+    }
+
     private static void deleteRecursively(File f) {
         File[] files = f.listFiles();
         if (files != null) {
@@ -1038,9 +1051,7 @@
     /**
      * Called by the Binder stub.
      */
-    UsageEvents queryEvents(int userId, long beginTime, long endTime,
-            boolean shouldObfuscateInstantApps, boolean shouldHideShortcutInvocationEvents,
-            boolean shouldHideLocusIdEvents) {
+    UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) {
         synchronized (mLock) {
             if (!mUserUnlockedStates.get(userId)) {
                 Slog.w(TAG, "Failed to query events for locked user " + userId);
@@ -1051,8 +1062,7 @@
             if (service == null) {
                 return null; // user was stopped or removed
             }
-            return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps,
-                    shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents);
+            return service.queryEvents(beginTime, endTime, flags);
         }
     }
 
@@ -1475,10 +1485,15 @@
             try {
                 final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
                         userId, callingPackage, callingPid, callingUid);
-                boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
-                return UsageStatsService.this.queryEvents(userId, beginTime, endTime,
-                        obfuscateInstantApps, hideShortcutInvocationEvents,
-                        shouldHideLocusIdEvents);
+                final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
+                final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
+                        callingPid, callingUid);
+                int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
+                if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
+                if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
+                if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
+                if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
+                return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1525,10 +1540,15 @@
             try {
                 final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
                         userId, callingPackage, callingPid, callingUid);
-                boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
-                return UsageStatsService.this.queryEvents(userId, beginTime, endTime,
-                        obfuscateInstantApps, hideShortcutInvocationEvents,
-                        shouldHideLocusIdEvents);
+                final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
+                        callingPid, callingUid);
+                boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
+                int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
+                if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
+                if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
+                if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
+                if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
+                return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2144,12 +2164,8 @@
         }
 
         @Override
-        public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime,
-                boolean obfuscateInstantApps, boolean shouldHideShortcutInvocationEvents,
-                boolean shouldHideLocusIdEvents) {
-            return UsageStatsService.this.queryEvents(
-                    userId, beginTime, endTime, obfuscateInstantApps,
-                    shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents);
+        public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime, int flags) {
+            return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d9317ac..db26d88 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -18,6 +18,10 @@
 
 import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.DEVICE_STARTUP;
+import static android.app.usage.UsageEvents.HIDE_LOCUS_EVENTS;
+import static android.app.usage.UsageEvents.HIDE_SHORTCUT_EVENTS;
+import static android.app.usage.UsageEvents.OBFUSCATE_INSTANT_APPS;
+import static android.app.usage.UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
 import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
 import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
 import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
@@ -481,8 +485,7 @@
         return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
     }
 
-    UsageEvents queryEvents(final long beginTime, final long endTime, boolean obfuscateInstantApps,
-            boolean hideShortcutInvocationEvents, boolean hideLocusIdEvents) {
+    UsageEvents queryEvents(final long beginTime, final long endTime, int flags) {
         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
             return null;
         }
@@ -500,15 +503,22 @@
                             }
 
                             Event event = stats.events.get(i);
-                            if (hideShortcutInvocationEvents
-                                    && event.mEventType == Event.SHORTCUT_INVOCATION) {
+                            final int eventType = event.mEventType;
+                            if (eventType == Event.SHORTCUT_INVOCATION
+                                    && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) {
                                 continue;
                             }
-                            if (hideLocusIdEvents
-                                    && event.mEventType == Event.LOCUS_ID_SET) {
+                            if (eventType == Event.LOCUS_ID_SET
+                                    && (flags & HIDE_LOCUS_EVENTS) == HIDE_LOCUS_EVENTS) {
                                 continue;
                             }
-                            if (obfuscateInstantApps) {
+                            if ((eventType == Event.NOTIFICATION_SEEN
+                                    || eventType == Event.NOTIFICATION_INTERRUPTION)
+                                    && (flags & OBFUSCATE_NOTIFICATION_EVENTS)
+                                    == OBFUSCATE_NOTIFICATION_EVENTS) {
+                                event = event.getObfuscatedNotificationEvent();
+                            }
+                            if ((flags & OBFUSCATE_INSTANT_APPS) == OBFUSCATE_INSTANT_APPS) {
                                 event = event.getObfuscatedIfInstantApp();
                             }
                             if (event.mPackage != null) {
diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml
index 41109b4..ef56fc8 100644
--- a/startop/iorap/functional_tests/AndroidTest.xml
+++ b/startop/iorap/functional_tests/AndroidTest.xml
@@ -34,6 +34,11 @@
         <option name="run-command" value="rm -r /data/misc/iorapd/*" />
         <option name="run-command" value="sleep 1" />
 
+        <!-- Set system properties to enable perfetto tracing, readahead and detailed logging. -->
+        <option name="run-command" value="setprop iorapd.perfetto.enable true" />
+        <option name="run-command" value="setprop iorapd.readahead.enable true" />
+        <option name="run-command" value="setprop iorapd.log.verbose true" />
+
         <option name="run-command" value="start iorapd" />
 
         <!-- give it some time to restart the service; otherwise the first unit test might fail -->
@@ -45,9 +50,5 @@
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
 
-    <!-- using DeviceSetup again does not work. we simply leave the device in a semi-bad
-         state. there is no way to clean this up as far as I know.
-         -->
-
 </configuration>
 
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
index bd8a45c..4002387 100644
--- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -67,7 +67,7 @@
   private static final String TEST_ACTIVITY_NAME = "com.android.settings.Settings";
 
   private static final String DB_PATH = "/data/misc/iorapd/sqlite.db";
-  private static final Duration TIMEOUT = Duration.ofSeconds(20L);
+  private static final Duration TIMEOUT = Duration.ofSeconds(300L);
 
   private static final String READAHEAD_INDICATOR =
       "Description = /data/misc/iorapd/com.android.settings/none/com.android.settings.Settings/compiled_traces/compiled_trace.pb";
@@ -88,7 +88,7 @@
     mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds());
   }
 
-  @Test
+  @Test (timeout = 300000)
   public void testApp() throws Exception {
     assertThat(mDevice, notNullValue());
 
@@ -247,7 +247,7 @@
       if (supplier.getAsBoolean()) {
         return true;
       }
-      TimeUnit.SECONDS.sleep(totalSleepTimeSeconds);
+      TimeUnit.SECONDS.sleep(sleepIntervalSeconds);
       totalSleepTimeSeconds += sleepIntervalSeconds;
       if (totalSleepTimeSeconds > timeout.getSeconds()) {
         return false;
@@ -367,7 +367,7 @@
    *
    * <p> This should be run as root.</p>
    */
-  private String executeShellCommand(String cmd) throws Exception {
+  private static String executeShellCommand(String cmd) throws Exception {
     Log.i(TAG, "Execute: " + cmd);
     return UiDevice.getInstance(
         InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index ec99f36..52213d8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -458,8 +459,14 @@
         /** Call supports the deflect feature. */
         public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
 
+        /**
+         * Call supports adding participants to the call via
+         * {@link #addConferenceParticipants(List)}.
+         * @hide
+         */
+        public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
         //******************************************************************************************
-        // Next CAPABILITY value: 0x02000000
+        // Next CAPABILITY value: 0x04000000
         //******************************************************************************************
 
         /**
@@ -539,7 +546,7 @@
          *
          * @see TelecomManager#EXTRA_USE_ASSISTED_DIALING
          */
-        public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
+        public static final int PROPERTY_ASSISTED_DIALING = 0x00000200;
 
         /**
          * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the
@@ -689,6 +696,9 @@
             if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
                 builder.append(" CAPABILITY_SUPPORT_DEFLECT");
             }
+            if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) {
+                builder.append(" CAPABILITY_ADD_PARTICIPANT");
+            }
             builder.append("]");
             return builder.toString();
         }
@@ -744,7 +754,7 @@
             if (hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
                 builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
             }
-            if (hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
+            if (hasProperty(properties, PROPERTY_ASSISTED_DIALING)) {
                 builder.append(" PROPERTY_ASSISTED_DIALING_USED");
             }
             if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
@@ -1703,6 +1713,17 @@
     }
 
     /**
+     * Pulls participants to existing call by forming a conference call.
+     * See {@link Details#CAPABILITY_ADD_PARTICIPANT}.
+     *
+     * @param participants participants to be pulled to existing call.
+     * @hide
+     */
+    public void addConferenceParticipants(@NonNull List<Uri> participants) {
+        mInCallAdapter.addConferenceParticipants(mTelecomCallId, participants);
+    }
+
+    /**
      * Initiates a request to the {@link ConnectionService} to pull an external call to the local
      * device.
      * <p>
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 56acdff..f019a9d 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,8 +16,13 @@
 
 package android.telecom;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.net.Uri;
@@ -319,6 +324,13 @@
     public void onConnectionAdded(Connection connection) {}
 
     /**
+     * Notifies the {@link Conference} of a request to add a new participants to the conference call
+     * @param participants that will be added to this conference call
+     * @hide
+     */
+    public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
+
+    /**
      * Notifies this Conference, which is in {@code STATE_RINGING}, of
      * a request to accept.
      * For managed {@link ConnectionService}s, this will be called when the user answers a call via
@@ -625,12 +637,12 @@
      * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
      * <p>
      * When setting the connection time, you should always set the connection elapsed time via
-     * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
+     * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected.
      *
      * @param connectionTimeMillis The connection time, in milliseconds, as returned by
      *                             {@link System#currentTimeMillis()}.
      */
-    public final void setConnectionTime(long connectionTimeMillis) {
+    public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) {
         mConnectTimeMillis = connectionTimeMillis;
     }
 
@@ -646,8 +658,28 @@
      *
      * @param connectionStartElapsedRealTime The connection time, as measured by
      * {@link SystemClock#elapsedRealtime()}.
+     * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead.
      */
+    @Deprecated
     public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
+        setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime);
+    }
+
+    /**
+     * Sets the start time of the {@link Conference} which is the basis for the determining the
+     * duration of the {@link Conference}.
+     * <p>
+     * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
+     * zone changes do not impact the conference duration.
+     * <p>
+     * When setting this, you should also set the connection time via
+     * {@link #setConnectionTime(long)}.
+     *
+     * @param connectionStartElapsedRealTime The connection time, as measured by
+     * {@link SystemClock#elapsedRealtime()}.
+     */
+    public final void setConnectionStartElapsedRealtimeMillis(
+            @ElapsedRealtimeLong long connectionStartElapsedRealTime) {
         mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
     }
 
@@ -668,7 +700,7 @@
      *
      * @return The time at which the {@code Conference} was connected.
      */
-    public final long getConnectionTime() {
+    public final @IntRange(from = 0) long getConnectionTime() {
         return mConnectTimeMillis;
     }
 
@@ -685,11 +717,8 @@
      * has no general use other than to the Telephony framework.
      *
      * @return The elapsed time at which the {@link Conference} was connected.
-     * @hide
      */
-    @SystemApi
-    @TestApi
-    public final long getConnectionStartElapsedRealTime() {
+    public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
         return mConnectionStartElapsedRealTime;
     }
 
@@ -987,6 +1016,7 @@
      */
     @SystemApi
     @TestApi
+    @RequiresPermission(MODIFY_PHONE_STATE)
     public void setConferenceState(boolean isConference) {
         for (Listener l : mListeners) {
             l.onConferenceStateChanged(this, isConference);
@@ -1007,6 +1037,7 @@
      */
     @SystemApi
     @TestApi
+    @RequiresPermission(MODIFY_PHONE_STATE)
     public final void setAddress(@NonNull Uri address,
             @TelecomManager.Presentation int presentation) {
         Log.d(this, "setAddress %s", address);
@@ -1113,12 +1144,52 @@
     }
 
     /**
-     * Sends an event associated with this {@code Conference} with associated event extras to the
-     * {@link InCallService} (note: this is identical in concept to
-     * {@link Connection#sendConnectionEvent(String, Bundle)}).
-     * @see Connection#sendConnectionEvent(String, Bundle)
+     * Sends an event associated with this {@link Conference} with associated event extras to the
+     * {@link InCallService}.
+     * <p>
+     * Connection events are used to communicate point in time information from a
+     * {@link ConnectionService} to an {@link InCallService} implementation.  An example of a
+     * custom connection event includes notifying the UI when a WIFI call has been handed over to
+     * LTE, which the InCall UI might use to inform the user that billing charges may apply.  The
+     * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE}
+     * connection event when a call to {@link Call#mergeConference()} has completed successfully.
+     * <p>
+     * Events are exposed to {@link InCallService} implementations via
+     * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+     * <p>
+     * No assumptions should be made as to how an In-Call UI or service will handle these events.
+     * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
+     * some events altogether.
+     * <p>
+     * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
+     * conflicts between {@link ConnectionService} implementations.  Further, custom
+     * {@link ConnectionService} implementations shall not re-purpose events in the
+     * {@code android.*} namespace, nor shall they define new event types in this namespace.  When
+     * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
+     * defined.  Extra keys for this bundle should be named similar to the event type (e.g.
+     * {@code com.example.extra.MY_EXTRA}).
+     * <p>
+     * When defining events and the associated extras, it is important to keep their behavior
+     * consistent when the associated {@link ConnectionService} is updated.  Support for deprecated
+     * events/extras should me maintained to ensure backwards compatibility with older
+     * {@link InCallService} implementations which were built to support the older behavior.
+     * <p>
+     * Expected connection events from the Telephony stack are:
+     * <p>
+     * <ul>
+     *      <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the
+     *      {@link Conference} could not be held.</li>
+     *      <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new
+     *      call is being merged into the conference.</li>
+     *      <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call
+     *      has completed being merged into the conference.</li>
+     *      <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new
+     *      call has failed to merge into the conference (the dialer app can determine which call
+     *      failed to merge based on the fact that the call still exists outside of the conference
+     *      at the end of the merge process).</li>
+     * </ul>
      *
-     * @param event The connection event.
+     * @param event The conference event.
      * @param extras Optional bundle containing extra information associated with the event.
      */
     public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8049459..3b0ba25 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,9 +16,14 @@
 
 package android.telecom;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.Notification;
@@ -376,8 +381,14 @@
     /** Call supports the deflect feature. */
     public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
 
+    /**
+     * When set, indicates that this {@link Connection} supports initiation of a conference call
+     * by directly adding participants using {@link #onAddConferenceParticipants(List)}.
+     * @hide
+     */
+    public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000;
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x04000000
+    // Next CAPABILITY value: 0x08000000
     //**********************************************************************************************
 
     /**
@@ -474,7 +485,7 @@
      *
      * @see TelecomManager#EXTRA_USE_ASSISTED_DIALING
      */
-    public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
+    public static final int PROPERTY_ASSISTED_DIALING = 1 << 9;
 
     /**
      * Set by the framework to indicate that the network has identified a Connection as an emergency
@@ -953,7 +964,9 @@
         if ((capabilities & CAPABILITY_SUPPORT_DEFLECT) == CAPABILITY_SUPPORT_DEFLECT) {
             builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
         }
-
+        if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) {
+            builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant");
+        }
         builder.append("]");
         return builder.toString();
     }
@@ -2109,19 +2122,24 @@
      */
     @SystemApi
     @TestApi
-    public final long getConnectTimeMillis() {
+    public final @IntRange(from = 0) long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
 
     /**
      * Retrieves the connection start time of the {@link Connection}, if specified.  A value of
      * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
-     * start time of the conference.
+     * start time of the connection.
      * <p>
      * Based on the value of {@link SystemClock#elapsedRealtime()}, which ensures that wall-clock
      * changes do not impact the call duration.
      * <p>
      * Used internally in Telephony when migrating conference participant data for IMS conferences.
+     * <p>
+     * The value returned is the same one set using
+     * {@link #setConnectionStartElapsedRealtimeMillis(long)}.  This value is never updated from
+     * the Telecom framework, so no permission enforcement occurs when retrieving the value with
+     * this method.
      *
      * @return The time at which the {@link Connection} was connected.
      *
@@ -2129,7 +2147,7 @@
      */
     @SystemApi
     @TestApi
-    public final long getConnectElapsedTimeMillis() {
+    public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
         return mConnectElapsedTimeMillis;
     }
 
@@ -2550,6 +2568,9 @@
      * Sets the time at which a call became active on this Connection. This is set only
      * when a conference call becomes active on this connection.
      * <p>
+     * This time corresponds to the date/time of connection and is stored in the call log in
+     * {@link android.provider.CallLog.Calls#DATE}.
+     * <p>
      * Used by telephony to maintain calls associated with an IMS Conference.
      *
      * @param connectTimeMillis The connection time, in milliseconds.  Should be set using a value
@@ -2559,7 +2580,8 @@
      */
     @SystemApi
     @TestApi
-    public final void setConnectTimeMillis(long connectTimeMillis) {
+    @RequiresPermission(MODIFY_PHONE_STATE)
+    public final void setConnectTimeMillis(@IntRange(from = 0) long connectTimeMillis) {
         mConnectTimeMillis = connectTimeMillis;
     }
 
@@ -2567,15 +2589,23 @@
      * Sets the time at which a call became active on this Connection. This is set only
      * when a conference call becomes active on this connection.
      * <p>
+     * This time is used to establish the duration of a call.  It uses
+     * {@link SystemClock#elapsedRealtime()} to ensure that the call duration is not impacted by
+     * time zone changes during a call.  The difference between the current
+     * {@link SystemClock#elapsedRealtime()} and the value set at the connection start time is used
+     * to populate {@link android.provider.CallLog.Calls#DURATION} in the call log.
+     * <p>
      * Used by telephony to maintain calls associated with an IMS Conference.
+     *
      * @param connectElapsedTimeMillis The connection time, in milliseconds.  Stored in the format
      *                              {@link SystemClock#elapsedRealtime()}.
-     *
      * @hide
      */
     @SystemApi
     @TestApi
-    public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
+    @RequiresPermission(MODIFY_PHONE_STATE)
+    public final void setConnectionStartElapsedRealtimeMillis(
+            @ElapsedRealtimeLong long connectElapsedTimeMillis) {
         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
     }
 
@@ -2953,6 +2983,14 @@
     public void onSeparate() {}
 
     /**
+     * Supports initiation of a conference call by directly adding participants to an ongoing call.
+     *
+     * @param participants with which conference call will be formed.
+     * @hide
+     */
+    public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
+
+    /**
      * Notifies this Connection of a request to abort.
      */
     public void onAbort() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 00c2918..2aea723 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -142,6 +141,7 @@
     private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC";
     private static final String SESSION_MERGE_CONFERENCE = "CS.mC";
     private static final String SESSION_SWAP_CONFERENCE = "CS.sC";
+    private static final String SESSION_ADD_PARTICIPANT = "CS.aP";
     private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
     private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
     private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
@@ -195,6 +195,7 @@
     private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
     private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
     private static final int MSG_REJECT_WITH_REASON = 38;
+    private static final int MSG_ADD_PARTICIPANT = 39;
 
     private static Connection sNullConnection;
 
@@ -627,6 +628,21 @@
         }
 
         @Override
+        public void addConferenceParticipants(String callId, List<Uri> participants,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = participants;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT);
             try {
@@ -1224,6 +1240,19 @@
                     }
                     break;
                 }
+                case MSG_ADD_PARTICIPANT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg3,
+                                SESSION_HANDLER + SESSION_ADD_PARTICIPANT);
+                        addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+
                 case MSG_ON_POST_DIAL_CONTINUE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -1778,7 +1807,7 @@
                         null : conference.getVideoProvider().getInterface(),
                 conference.getVideoState(),
                 conference.getConnectTimeMillis(),
-                conference.getConnectionStartElapsedRealTime(),
+                conference.getConnectionStartElapsedRealtimeMillis(),
                 conference.getStatusHints(),
                 conference.getExtras(),
                 conference.getAddress(),
@@ -1884,7 +1913,7 @@
                         connection.isRingbackRequested(),
                         connection.getAudioModeIsVoip(),
                         connection.getConnectTimeMillis(),
-                        connection.getConnectElapsedTimeMillis(),
+                        connection.getConnectionStartElapsedRealtimeMillis(),
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
                         createIdList(connection.getConferenceables()),
@@ -2152,6 +2181,17 @@
         }
     }
 
+    private void addConferenceParticipants(String callId, List<Uri> participants) {
+        Log.d(this, "addConferenceParticipants(%s)", callId);
+        if (mConnectionById.containsKey(callId)) {
+            findConnectionForAction(callId, "addConferenceParticipants")
+                    .onAddConferenceParticipants(participants);
+        } else {
+            findConferenceForAction(callId, "addConferenceParticipants")
+                    .onAddConferenceParticipants(participants);
+        }
+    }
+
     /**
      * Notifies a {@link Connection} of a request to pull an external call.
      *
@@ -2374,7 +2414,7 @@
                             null : conference.getVideoProvider().getInterface(),
                     conference.getVideoState(),
                     conference.getConnectTimeMillis(),
-                    conference.getConnectionStartElapsedRealTime(),
+                    conference.getConnectionStartElapsedRealtimeMillis(),
                     conference.getStatusHints(),
                     conference.getExtras(),
                     conference.getAddress(),
@@ -2465,7 +2505,7 @@
                     connection.isRingbackRequested(),
                     connection.getAudioModeIsVoip(),
                     connection.getConnectTimeMillis(),
-                    connection.getConnectElapsedTimeMillis(),
+                    connection.getConnectionStartElapsedRealtimeMillis(),
                     connection.getStatusHints(),
                     connection.getDisconnectCause(),
                     emptyList,
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 594c1eb..9d291740 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -283,6 +283,20 @@
     }
 
     /**
+     * Instructs Telecom to pull participants to existing call
+     *
+     * @param callId The unique ID of the call.
+     * @param participants participants to be pulled to existing call.
+     */
+    public void addConferenceParticipants(String callId, List<Uri> participants) {
+        try {
+            mAdapter.addConferenceParticipants(callId, participants);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+
+    /**
      * Instructs Telecom to split the specified call from any conference call with which it may be
      * connected.
      *
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index ebfa3a1..982e5f3 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.app.UiModeManager;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
 import android.hardware.camera2.CameraManager;
@@ -43,12 +44,32 @@
  * phone calls.
  * <h2>Becoming the Default Phone App</h2>
  * The default dialer/phone app is one which provides the in-call user interface while the device is
- * in a call.  A device is bundled with a system provided default dialer/phone app.  The user may
- * choose a single app to take over this role from the system app.  An app which wishes to fulfill
- * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role.
+ * in a call.  It also provides the user with a means to initiate calls and see a history of calls
+ * on their device.  A device is bundled with a system provided default dialer/phone app.  The user
+ * may choose a single app to take over this role from the system app.  An app which wishes to
+ * fulfill one this role uses the {@link android.app.role.RoleManager} to request that they fill the
+ * {@link android.app.role.RoleManager#ROLE_DIALER} role.
  * <p>
- * An app filling the role of the default phone app provides a user interface while the device is in
- * a call, and the device is not in car mode.
+ * The default phone app provides a user interface while the device is in a call, and the device is
+ * not in car mode (i.e. {@link UiModeManager#getCurrentModeType()} is not
+ * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}).
+ * <p>
+ * In order to fill the {@link android.app.role.RoleManager#ROLE_DIALER} role, an app must meet a
+ * number of requirements:
+ * <ul>
+ *     <li>It must handle the {@link Intent#ACTION_DIAL} intent.  This means the app must provide
+ *     a dial pad UI for the user to initiate outgoing calls.</li>
+ *     <li>It must fully implement the {@link InCallService} API and provide both an incoming call
+ *     UI, as well as an ongoing call UI.</li>
+ * </ul>
+ * <p>
+ * Note: If the app filling the {@link android.app.role.RoleManager#ROLE_DIALER} crashes during
+ * {@link InCallService} binding, the Telecom framework will automatically fall back to using the
+ * dialer app pre-loaded on the device.  The system will display a notification to the user to let
+ * them know that the app has crashed and that their call was continued using the pre-loaded dialer
+ * app.
+ * <p>
+ * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
  * <p>
  * Below is an example manifest registration for an {@code InCallService}. The meta-data
  * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
@@ -82,6 +103,11 @@
  *           <action android:name="android.intent.action.DIAL" />
  *           <category android:name="android.intent.category.DEFAULT" />
  *      </intent-filter>
+ *      <intent-filter>
+ *           <action android:name="android.intent.action.DIAL" />
+ *           <category android:name="android.intent.category.DEFAULT" />
+ *           <data android:scheme="tel" />
+ *      </intent-filter>
  * </activity>
  * }
  * </pre>
@@ -111,6 +137,7 @@
  *         }
  *     }
  * }
+ * }
  * </pre>
  * <p id="incomingCallNotification">
  * <h3>Showing the Incoming Call Notification</h3>
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index f00432b..4e6e1a5 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -16,7 +16,10 @@
 
 package android.telecom;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Intent;
@@ -614,7 +617,8 @@
          * time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only
          * grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced.
          * <p>
-         * Note: This is an API specific to the Telephony stack.
+         * Note: This is an API specific to the Telephony stack; the group Id will be ignored for
+         * callers not holding the correct permission.
          *
          * @param groupId The group Id of the {@link PhoneAccount} that will replace any other
          * registered {@link PhoneAccount} in Telecom with the same Group Id.
@@ -623,6 +627,7 @@
          */
         @SystemApi
         @TestApi
+        @RequiresPermission(MODIFY_PHONE_STATE)
         public @NonNull Builder setGroupId(@NonNull String groupId) {
             if (groupId != null) {
                 mGroupId = groupId;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a28cc4f..5d7d649 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -819,8 +819,8 @@
      * automatically add dialing prefixes when placing international calls.
      * <p>
      * Setting this extra on the outgoing call extras will cause the
-     * {@link Connection#PROPERTY_ASSISTED_DIALING_USED} property and
-     * {@link Call.Details#PROPERTY_ASSISTED_DIALING_USED} property to be set on the
+     * {@link Connection#PROPERTY_ASSISTED_DIALING} property and
+     * {@link Call.Details#PROPERTY_ASSISTED_DIALING} property to be set on the
      * {@link Connection}/{@link Call} in question.  When the call is logged to the call log, the
      * {@link android.provider.CallLog.Calls#FEATURES_ASSISTED_DIALING_USED} call feature is set to
      * indicate that assisted dialing was used for the call.
@@ -1412,7 +1412,7 @@
     /**
      * Used to determine the currently selected default dialer package for a specific user.
      *
-     * @param userId the user id to query the default dialer package for.
+     * @param userHandle the user id to query the default dialer package for.
      * @return package name for the default dialer package or null if no package has been
      *         selected as the default dialer.
      * @hide
@@ -1420,10 +1420,11 @@
     @SystemApi
     @TestApi
     @RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
-    public @Nullable String getDefaultDialerPackage(int userId) {
+    public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) {
         try {
             if (isServiceConnected()) {
-                return getTelecomService().getDefaultDialerPackageForUser(userId);
+                return getTelecomService().getDefaultDialerPackageForUser(
+                        userHandle.getIdentifier());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 4249dff..a397d77 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -104,6 +104,9 @@
 
     void swapConference(String conferenceCallId, in Session.Info sessionInfo);
 
+    void addConferenceParticipants(String CallId, in List<Uri> participants,
+    in Session.Info sessionInfo);
+
     void onPostDialContinue(String callId, boolean proceed, in Session.Info sessionInfo);
 
     void pullExternalCall(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index eb2d714..9beff22 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -67,6 +67,8 @@
 
     void swapConference(String callId);
 
+    void addConferenceParticipants(String callId, in List<Uri> participants);
+
     void turnOnProximitySensor();
 
     void turnOffProximitySensor(boolean screenOnImmediately);
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index d2a5905..a27c480 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -661,4 +661,16 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Skip464XlatStatus {}
+
+    /**
+     * Override network type
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "OVERRIDE_NETWORK_TYPE_", value = {
+            DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+            DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+            DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
+            DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+            DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
+    public @interface OverrideNetworkType {}
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ebb53c5..51b4a31 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1090,6 +1090,14 @@
             "support_adhoc_conference_calls_bool";
 
     /**
+     * Determines whether conference participants can be added to existing call.  When {@code true},
+     * adding conference participants to existing call is supported, {@code false otherwise}.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL =
+            "support_add_conference_participants_bool";
+
+    /**
      * Determines whether conference calls are supported by a carrier.  When {@code true},
      * conference calling is supported, {@code false otherwise}.
      */
@@ -4004,6 +4012,7 @@
         sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/DisplayInfo.aidl b/telephony/java/android/telephony/DisplayInfo.aidl
new file mode 100644
index 0000000..861b0fe
--- /dev/null
+++ b/telephony/java/android/telephony/DisplayInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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.telephony;
+
+parcelable DisplayInfo;
diff --git a/telephony/java/android/telephony/DisplayInfo.java b/telephony/java/android/telephony/DisplayInfo.java
new file mode 100644
index 0000000..d54bcf9
--- /dev/null
+++ b/telephony/java/android/telephony/DisplayInfo.java
@@ -0,0 +1,172 @@
+/*
+ * 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
+ *
+ *      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.telephony;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.OverrideNetworkType;
+
+import java.util.Objects;
+
+/**
+ * DisplayInfo contains telephony-related information used for display purposes only. This
+ * information is provided in accordance with carrier policy and branding preferences; it is not
+ * necessarily a precise or accurate representation of the current state and should be treated
+ * accordingly.
+ */
+public final class DisplayInfo implements Parcelable {
+    /**
+     * No override. {@link #getNetworkType()} should be used for display network
+     * type.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_NONE = 0;
+
+    /**
+     * Override network type when the device is connected to
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network and is using carrier aggregation.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1;
+
+    /**
+     * Override network type when the device is connected to advanced pro
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2;
+
+    /**
+     * Override network type when the device is connected to
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
+     * capability or is currently connected to the secondary
+     * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3;
+
+    /**
+     * Override network type when the device is connected to
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
+     * capability or is currently connected to the secondary
+     * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
+     *
+     * @see AccessNetworkConstants.NgranBands#FREQUENCY_RANGE_GROUP_2
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
+
+    @NetworkType
+    private final  int mNetworkType;
+
+    @OverrideNetworkType
+    private final  int mOverrideNetworkType;
+
+    /**
+     * Constructor
+     *
+     * @param networkType Current packet-switching cellular network type
+     * @param overrideNetworkType The override network type
+     *
+     * @hide
+     */
+    public DisplayInfo(@NetworkType int networkType, @OverrideNetworkType int overrideNetworkType) {
+        mNetworkType = networkType;
+        mOverrideNetworkType = overrideNetworkType;
+    }
+
+    /** @hide */
+    public DisplayInfo(Parcel p) {
+        mNetworkType = p.readInt();
+        mOverrideNetworkType = p.readInt();
+    }
+
+    /**
+     * Get current packet-switching cellular network type. This is the actual network type the
+     * device is camped on.
+     *
+     * @return The network type.
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Get the override network type. Note the override network type is for market branding
+     * or visualization purposes only. It cannot be treated as the actual network type device is
+     * camped on.
+     *
+     * @return The override network type.
+     */
+    @OverrideNetworkType
+    public int getOverrideNetworkType() {
+        return mOverrideNetworkType;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mNetworkType);
+        dest.writeInt(mOverrideNetworkType);
+    }
+
+    public static final @NonNull Parcelable.Creator<DisplayInfo> CREATOR =
+            new Parcelable.Creator<DisplayInfo>() {
+                @Override
+                public DisplayInfo createFromParcel(Parcel source) {
+                    return new DisplayInfo(source);
+                }
+
+                @Override
+                public DisplayInfo[] newArray(int size) {
+                    return new DisplayInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DisplayInfo that = (DisplayInfo) o;
+        return mNetworkType == that.mNetworkType
+                && mOverrideNetworkType == that.mOverrideNetworkType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNetworkType, mOverrideNetworkType);
+    }
+
+    private static String overrideNetworkTypeToString(@OverrideNetworkType int type) {
+        switch (type) {
+            case OVERRIDE_NETWORK_TYPE_NONE: return "NONE";
+            case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA";
+            case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO";
+            case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA";
+            case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE";
+            default: return "UNKNOWN";
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "DisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
+                + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType);
+    }
+}
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 54980a2..250d9e8 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -256,337 +256,6 @@
     /** Access Blocked by CDMA network. */
     public static final int CDMA_ACCESS_BLOCKED                              = 1009;
 
-    /** Mapped from ImsReasonInfo */
-    // TODO: remove ImsReasonInfo from preciseDisconnectCause
-    /* The passed argument is an invalid */
-    /** @hide */
-    public static final int LOCAL_ILLEGAL_ARGUMENT                           = 1200;
-    // The operation is invoked in invalid call state
-    /** @hide */
-    public static final int LOCAL_ILLEGAL_STATE                              = 1201;
-    // IMS service internal error
-    /** @hide */
-    public static final int LOCAL_INTERNAL_ERROR                             = 1202;
-    // IMS service goes down (service connection is lost)
-    /** @hide */
-    public static final int LOCAL_IMS_SERVICE_DOWN                           = 1203;
-    // No pending incoming call exists
-    /** @hide */
-    public static final int LOCAL_NO_PENDING_CALL                            = 1204;
-    // Service unavailable; by power off
-    /** @hide */
-    public static final int LOCAL_POWER_OFF                                  = 1205;
-    // Service unavailable; by low battery
-    /** @hide */
-    public static final int LOCAL_LOW_BATTERY                                = 1206;
-    // Service unavailable; by out of service (data service state)
-    /** @hide */
-    public static final int LOCAL_NETWORK_NO_SERVICE                         = 1207;
-    /* Service unavailable; by no LTE coverage
-     * (VoLTE is not supported even though IMS is registered)
-     */
-    /** @hide */
-    public static final int LOCAL_NETWORK_NO_LTE_COVERAGE                    = 1208;
-    /** Service unavailable; by located in roaming area */
-    /** @hide */
-    public static final int LOCAL_NETWORK_ROAMING                            = 1209;
-    /** Service unavailable; by IP changed */
-    /** @hide */
-    public static final int LOCAL_NETWORK_IP_CHANGED                         = 1210;
-    /** Service unavailable; other */
-    /** @hide */
-    public static final int LOCAL_SERVICE_UNAVAILABLE                        = 1211;
-    /* Service unavailable; IMS connection is lost (IMS is not registered) */
-    /** @hide */
-    public static final int LOCAL_NOT_REGISTERED                             = 1212;
-    /** Max call exceeded */
-    /** @hide */
-    public static final int LOCAL_MAX_CALL_EXCEEDED                          = 1213;
-    /** Call decline */
-    /** @hide */
-    public static final int LOCAL_CALL_DECLINE                               = 1214;
-    /** SRVCC is in progress */
-    /** @hide */
-    public static final int LOCAL_CALL_VCC_ON_PROGRESSING                    = 1215;
-    /** Resource reservation is failed (QoS precondition) */
-    /** @hide */
-    public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED           = 1216;
-    /** Retry CS call; VoLTE service can't be provided by the network or remote end
-     *  Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
-     *  @hide
-     */
-    public static final int LOCAL_CALL_CS_RETRY_REQUIRED                     = 1217;
-    /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
-    /** @hide */
-    public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED                  = 1218;
-    /** IMS call is already terminated (in TERMINATED state) */
-    /** @hide */
-    public static final int LOCAL_CALL_TERMINATED                            = 1219;
-    /** Handover not feasible */
-    /** @hide */
-    public static final int LOCAL_HO_NOT_FEASIBLE                            = 1220;
-
-    /** 1xx waiting timer is expired after sending INVITE request (MO only) */
-    /** @hide */
-    public static final int TIMEOUT_1XX_WAITING                              = 1221;
-    /** User no answer during call setup operation (MO/MT)
-     *  MO : 200 OK to INVITE request is not received,
-     *  MT : No action from user after alerting the call
-     *  @hide
-     */
-    public static final int TIMEOUT_NO_ANSWER                                = 1222;
-    /** User no answer during call update operation (MO/MT)
-     *  MO : 200 OK to re-INVITE request is not received,
-     *  MT : No action from user after alerting the call
-     *  @hide
-     */
-    public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE                    = 1223;
-
-    /**
-     * STATUSCODE (SIP response code) (IMS -> Telephony)
-     */
-    /** SIP request is redirected */
-    /** @hide */
-    public static final int SIP_REDIRECTED                                   = 1300;
-    /** 4xx responses */
-    /** 400 : Bad Request */
-    /** @hide */
-    public static final int SIP_BAD_REQUEST                                  = 1310;
-    /** 403 : Forbidden */
-    /** @hide */
-    public static final int SIP_FORBIDDEN                                    = 1311;
-    /** 404 : Not Found */
-    /** @hide */
-    public static final int SIP_NOT_FOUND                                    = 1312;
-    /** 415 : Unsupported Media Type
-     *  416 : Unsupported URI Scheme
-     *  420 : Bad Extension
-     */
-    /** @hide */
-    public static final int SIP_NOT_SUPPORTED                                = 1313;
-    /** 408 : Request Timeout */
-    /** @hide */
-    public static final int SIP_REQUEST_TIMEOUT                              = 1314;
-    /** 480 : Temporarily Unavailable */
-    /** @hide */
-    public static final int SIP_TEMPRARILY_UNAVAILABLE                       = 1315;
-    /** 484 : Address Incomplete */
-    /** @hide */
-    public static final int SIP_BAD_ADDRESS                                  = 1316;
-    /** 486 : Busy Here
-     *  600 : Busy Everywhere
-     */
-    /** @hide */
-    public static final int SIP_BUSY                                         = 1317;
-    /** 487 : Request Terminated */
-    /** @hide */
-    public static final int SIP_REQUEST_CANCELLED                            = 1318;
-    /** 406 : Not Acceptable
-     *  488 : Not Acceptable Here
-     *  606 : Not Acceptable
-     */
-    /** @hide */
-    public static final int SIP_NOT_ACCEPTABLE                               = 1319;
-    /** 410 : Gone
-     *  604 : Does Not Exist Anywhere
-     */
-    /** @hide */
-    public static final int SIP_NOT_REACHABLE                                = 1320;
-    /** Others */
-    /** @hide */
-    public static final int SIP_CLIENT_ERROR                                 = 1321;
-    /** 481 : Transaction Does Not Exist */
-    /** @hide */
-    public static final int SIP_TRANSACTION_DOES_NOT_EXIST                   = 1322;
-    /** 5xx responses
-     *  501 : Server Internal Error
-     */
-    /** @hide */
-    public static final int SIP_SERVER_INTERNAL_ERROR                        = 1330;
-    /** 503 : Service Unavailable */
-    /** @hide */
-    public static final int SIP_SERVICE_UNAVAILABLE                          = 1331;
-    /** 504 : Server Time-out */
-    /** @hide */
-    public static final int SIP_SERVER_TIMEOUT                               = 1332;
-    /** Others */
-    /** @hide */
-    public static final int SIP_SERVER_ERROR                                 = 1333;
-    /** 6xx responses
-     *  603 : Decline
-     */
-    /** @hide */
-    public static final int SIP_USER_REJECTED                                = 1340;
-    /** Others */
-    /** @hide */
-    public static final int SIP_GLOBAL_ERROR                                 = 1341;
-    /** Emergency failure */
-    /** @hide */
-    public static final int EMERGENCY_TEMP_FAILURE                           = 1342;
-    /** @hide */
-    public static final int EMERGENCY_PERM_FAILURE                           = 1343;
-    /** Media resource initialization failed */
-    /** @hide */
-    public static final int MEDIA_INIT_FAILED                                = 1400;
-    /** RTP timeout (no audio / video traffic in the session) */
-    /** @hide */
-    public static final int MEDIA_NO_DATA                                    = 1401;
-    /** Media is not supported; so dropped the call */
-    /** @hide */
-    public static final int MEDIA_NOT_ACCEPTABLE                             = 1402;
-    /** Unknown media related errors */
-    /** @hide */
-    public static final int MEDIA_UNSPECIFIED                                = 1403;
-    /** User triggers the call end */
-    /** @hide */
-    public static final int USER_TERMINATED                                  = 1500;
-    /** No action while an incoming call is ringing */
-    /** @hide */
-    public static final int USER_NOANSWER                                    = 1501;
-    /** User ignores an incoming call */
-    /** @hide */
-    public static final int USER_IGNORE                                      = 1502;
-    /** User declines an incoming call */
-    /** @hide */
-    public static final int USER_DECLINE                                     = 1503;
-    /** Device declines/ends a call due to low battery */
-    /** @hide */
-    public static final int LOW_BATTERY                                      = 1504;
-    /** Device declines call due to blacklisted call ID */
-    /** @hide */
-    public static final int BLACKLISTED_CALL_ID                              = 1505;
-    /** The call is terminated by the network or remote user */
-    /** @hide */
-    public static final int USER_TERMINATED_BY_REMOTE                        = 1510;
-
-    /**
-     * UT
-     */
-    /** @hide */
-    public static final int UT_NOT_SUPPORTED                                 = 1800;
-    /** @hide */
-    public static final int UT_SERVICE_UNAVAILABLE                           = 1801;
-    /** @hide */
-    public static final int UT_OPERATION_NOT_ALLOWED                         = 1802;
-    /** @hide */
-    public static final int UT_NETWORK_ERROR                                 = 1803;
-    /** @hide */
-    public static final int UT_CB_PASSWORD_MISMATCH                          = 1804;
-
-    /**
-     * ECBM
-     * @hide
-     */
-    public static final int ECBM_NOT_SUPPORTED                               = 1900;
-
-    /**
-     * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
-     * @hide
-     */
-    public static final int MULTIENDPOINT_NOT_SUPPORTED                      = 1901;
-
-    /**
-     * CALL DROP error codes (Call could drop because of many reasons like Network not available,
-     *  handover, failed, etc)
-     */
-
-    /**
-     * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
-     * active wifi call and at the edge of coverage and there is no qualified LTE network available
-     * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
-     * code is received as part of the handover message.
-     * @hide
-     */
-    public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE               = 2000;
-
-    /**
-     * MT call has ended due to a release from the network
-     * because the call was answered elsewhere
-     * @hide
-     */
-    public static final int ANSWERED_ELSEWHERE                               = 2100;
-
-    /**
-     * For MultiEndpoint - Call Pull request has failed
-     * @hide
-     */
-    public static final int CALL_PULL_OUT_OF_SYNC                            = 2101;
-
-    /**
-     * For MultiEndpoint - Call has been pulled from primary to secondary
-     * @hide
-     */
-    public static final int CALL_PULLED                                      = 2102;
-
-    /**
-     * Supplementary services (HOLD/RESUME) failure error codes.
-     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
-     * @hide
-     */
-    public static final int SUPP_SVC_FAILED                                  = 2300;
-    /** @hide */
-    public static final int SUPP_SVC_CANCELLED                               = 2301;
-    /** @hide */
-    public static final int SUPP_SVC_REINVITE_COLLISION                      = 2302;
-
-    /**
-     * DPD Procedure received no response or send failed
-     * @hide
-     */
-    public static final int IWLAN_DPD_FAILURE                                = 2400;
-
-    /**
-     * Establishment of the ePDG Tunnel Failed
-     * @hide
-     */
-    public static final int EPDG_TUNNEL_ESTABLISH_FAILURE                    = 2500;
-
-    /**
-     * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
-     * @hide
-     */
-    public static final int EPDG_TUNNEL_REKEY_FAILURE                        = 2501;
-
-    /**
-     * Connection to the packet gateway is lost
-     * @hide
-     */
-    public static final int EPDG_TUNNEL_LOST_CONNECTION                      = 2502;
-
-    /**
-     * The maximum number of calls allowed has been reached.  Used in a multi-endpoint scenario
-     * where the number of calls across all connected devices has reached the maximum.
-     * @hide
-     */
-    public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED                  = 2503;
-
-    /**
-     * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
-     * declined the call.  Used in a multi-endpoint scenario where a remote device declined an
-     * incoming call.
-     * @hide
-     */
-    public static final int REMOTE_CALL_DECLINE                              = 2504;
-
-    /**
-     * Indicates the call was disconnected due to the user reaching their data limit.
-     * @hide
-     */
-    public static final int DATA_LIMIT_REACHED                               = 2505;
-
-    /**
-     * Indicates the call was disconnected due to the user disabling cellular data.
-     * @hide
-     */
-    public static final int DATA_DISABLED                                    = 2506;
-
-    /**
-     * Indicates a call was disconnected due to loss of wifi signal.
-     * @hide
-     */
-    public static final int WIFI_LOST                                        = 2507;
-
-
     /* OEM specific error codes. To be used by OEMs when they don't want to
        reveal error code which would be replaced by ERROR_UNSPECIFIED */
     public static final int OEM_CAUSE_1                                      = 0xf001;
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 20d0e96..ae20cae 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -430,10 +430,9 @@
     private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException {
         String apkPath = getApkPath(TARGET_PACKAGE);
         String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
+        // Exclude directories since we only care about files.
         HashSet<String> actualFiles = new HashSet<>(Arrays.asList(
-                expectRemoteCommandToSucceed("ls " + appDir).split("\n")));
-        assertTrue(actualFiles.remove("lib"));
-        assertTrue(actualFiles.remove("oat"));
+                expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n")));
 
         HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames));
         assertEquals(expectedFiles, actualFiles);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 155c61f..eb78529 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -148,6 +148,7 @@
     @Mock private AppOpsManager mAppOps;
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
+    @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
     @Mock private ConnectivityManager mConnectivityManager;
     @Mock private KeyStore mKeyStore;
     private final VpnProfile mVpnProfile = new VpnProfile("key");
@@ -867,7 +868,8 @@
      * Mock some methods of vpn object.
      */
     private Vpn createVpn(@UserIdInt int userId) {
-        return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
+        return new Vpn(Looper.myLooper(), mContext, mNetService,
+                userId, mSystemServices, mIkev2SessionCreator);
     }
 
     private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b6f4490..f693315 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -28,6 +28,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.ActivityManager;
@@ -1621,11 +1622,13 @@
          * @param wifiConfiguration WifiConfiguration object corresponding to the network
          *                          user selected.
          */
+        @SuppressLint("CallbackMethodName")
         default void select(@NonNull WifiConfiguration wifiConfiguration) {}
 
         /**
          * User rejected the app's request.
          */
+        @SuppressLint("CallbackMethodName")
         default void reject() {}
     }
 
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 9990180..9c01d36 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -738,7 +738,16 @@
         @Override
         public String toString() {
             StringBuilder builder = new StringBuilder();
-            builder.append("IMSI: ").append(mImsi).append("\n");
+            String imsi;
+            if (mImsi != null) {
+                if (mImsi.length() > 6 && mImsi.charAt(6) != '*') {
+                    // Truncate the full IMSI from the log
+                    imsi = mImsi.substring(0, 6) + "****";
+                } else {
+                    imsi = mImsi;
+                }
+                builder.append("IMSI: ").append(imsi).append("\n");
+            }
             builder.append("EAPType: ").append(mEapType).append("\n");
             return builder.toString();
         }
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java
similarity index 98%
rename from wifi/java/android/net/wifi/wificond/WifiCondManager.java
rename to wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java
index 61f18e0..89f642f 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java
@@ -49,15 +49,16 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The
+ * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework - used
+ * to encapsulate the Wi-Fi 80211nl management interface. The
  * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions.
  *
  * @hide
  */
 @SystemApi
-@SystemService(Context.WIFI_COND_SERVICE)
-public class WifiCondManager {
-    private static final String TAG = "WifiCondManager";
+@SystemService(Context.WIFI_NL80211_SERVICE)
+public class WifiNl80211Manager {
+    private static final String TAG = "WifiNl80211Manager";
     private boolean mVerboseLoggingEnabled = false;
 
     /**
@@ -316,14 +317,14 @@
     public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5;
 
     /** @hide */
-    public WifiCondManager(Context context) {
+    public WifiNl80211Manager(Context context) {
         mAlarmManager = context.getSystemService(AlarmManager.class);
         mEventHandler = new Handler(context.getMainLooper());
     }
 
     /** @hide */
     @VisibleForTesting
-    public WifiCondManager(Context context, IWificond wificond) {
+    public WifiNl80211Manager(Context context, IWificond wificond) {
         this(context);
         mWificond = wificond;
     }
@@ -485,7 +486,7 @@
     }
 
     /**
-     * Enable or disable verbose logging of the WifiCondManager module.
+     * Enable or disable verbose logging of the WifiNl80211Manager module.
      * @param enable True to enable verbose logging. False to disable verbose logging.
      */
     public void enableVerboseLogging(boolean enable) {
@@ -493,7 +494,7 @@
     }
 
     /**
-     * Register a death notification for the WifiCondManager which acts as a proxy for the
+     * Register a death notification for the WifiNl80211Manager which acts as a proxy for the
      * wificond daemon (i.e. the death listener will be called when and if the wificond daemon
      * dies).
      *
@@ -518,7 +519,7 @@
             // We already have a wificond handle.
             return true;
         }
-        IBinder binder = ServiceManager.getService(Context.WIFI_COND_SERVICE);
+        IBinder binder = ServiceManager.getService(Context.WIFI_NL80211_SERVICE);
         mWificond = IWificond.Stub.asInterface(binder);
         if (mWificond == null) {
             Log.e(TAG, "Failed to get reference to wificond");
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java
similarity index 95%
rename from wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
rename to wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java
index b745a34..a818406 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java
@@ -71,10 +71,10 @@
 import java.util.Set;
 
 /**
- * Unit tests for {@link android.net.wifi.WifiCondManager}.
+ * Unit tests for {@link android.net.wifi.wificond.WifiNl80211Manager}.
  */
 @SmallTest
-public class WifiCondManagerTest {
+public class WifiNl80211ManagerTest {
     @Mock
     private IWificond mWificond;
     @Mock
@@ -86,21 +86,21 @@
     @Mock
     private IApInterface mApInterface;
     @Mock
-    private WifiCondManager.SoftApCallback mSoftApListener;
+    private WifiNl80211Manager.SoftApCallback mSoftApListener;
     @Mock
-    private WifiCondManager.SendMgmtFrameCallback mSendMgmtFrameCallback;
+    private WifiNl80211Manager.SendMgmtFrameCallback mSendMgmtFrameCallback;
     @Mock
-    private WifiCondManager.ScanEventCallback mNormalScanCallback;
+    private WifiNl80211Manager.ScanEventCallback mNormalScanCallback;
     @Mock
-    private WifiCondManager.ScanEventCallback mPnoScanCallback;
+    private WifiNl80211Manager.ScanEventCallback mPnoScanCallback;
     @Mock
-    private WifiCondManager.PnoScanRequestCallback mPnoScanRequestCallback;
+    private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback;
     @Mock
     private Context mContext;
     private TestLooper mLooper;
     private TestAlarmManager mTestAlarmManager;
     private AlarmManager mAlarmManager;
-    private WifiCondManager mWificondControl;
+    private WifiNl80211Manager mWificondControl;
     private static final String TEST_INTERFACE_NAME = "test_wlan_if";
     private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1";
     private static final String TEST_INVALID_INTERFACE_NAME = "asdf";
@@ -180,7 +180,7 @@
         when(mWificond.tearDownApInterface(any())).thenReturn(true);
         when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl);
         when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
-        mWificondControl = new WifiCondManager(mContext, mWificond);
+        mWificondControl = new WifiNl80211Manager(mContext, mWificond);
         assertEquals(true,
                 mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run,
                         mNormalScanCallback, mPnoScanCallback));
@@ -492,7 +492,7 @@
         // getScanResults should fail.
         assertEquals(0,
                 mWificondControl.getScanResults(TEST_INTERFACE_NAME,
-                        WifiCondManager.SCAN_TYPE_SINGLE_SCAN).size());
+                        WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN).size());
     }
 
     /**
@@ -786,10 +786,10 @@
      */
     @Test
     public void testSendMgmtFrameCalledTwiceBeforeFinished() throws Exception {
-        WifiCondManager.SendMgmtFrameCallback cb1 = mock(
-                WifiCondManager.SendMgmtFrameCallback.class);
-        WifiCondManager.SendMgmtFrameCallback cb2 = mock(
-                WifiCondManager.SendMgmtFrameCallback.class);
+        WifiNl80211Manager.SendMgmtFrameCallback cb1 = mock(
+                WifiNl80211Manager.SendMgmtFrameCallback.class);
+        WifiNl80211Manager.SendMgmtFrameCallback cb2 = mock(
+                WifiNl80211Manager.SendMgmtFrameCallback.class);
 
         mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
                 Runnable::run, cb1);
@@ -800,7 +800,7 @@
 
         mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
                 Runnable::run, cb2);
-        verify(cb2).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED);
+        verify(cb2).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED);
         // verify SendMgmtFrame() still was only called once i.e. not called again
         verify(mClientInterface, times(1))
                 .SendMgmtFrame(any(), any(), anyInt());
@@ -811,8 +811,8 @@
      */
     @Test
     public void testSendMgmtFrameThrowsException() throws Exception {
-        WifiCondManager.SendMgmtFrameCallback cb = mock(
-                WifiCondManager.SendMgmtFrameCallback.class);
+        WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+                WifiNl80211Manager.SendMgmtFrameCallback.class);
 
         final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
                 ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -834,7 +834,7 @@
         verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue()));
 
         sendMgmtFrameEventCaptor.getValue().OnFailure(
-                WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+                WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
         mLooper.dispatchAll();
 
         handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
@@ -848,8 +848,8 @@
      */
     @Test
     public void testSendMgmtFrameSuccess() throws Exception {
-        WifiCondManager.SendMgmtFrameCallback cb = mock(
-                WifiCondManager.SendMgmtFrameCallback.class);
+        WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+                WifiNl80211Manager.SendMgmtFrameCallback.class);
 
         final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
                 ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -882,8 +882,8 @@
      */
     @Test
     public void testSendMgmtFrameFailure() throws Exception {
-        WifiCondManager.SendMgmtFrameCallback cb = mock(
-                WifiCondManager.SendMgmtFrameCallback.class);
+        WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+                WifiNl80211Manager.SendMgmtFrameCallback.class);
 
         final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
                 ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -898,10 +898,10 @@
                 Runnable::run, cb);
 
         sendMgmtFrameEventCaptor.getValue().OnFailure(
-                WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+                WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
         mLooper.dispatchAll();
         verify(cb, never()).onAck(anyInt());
-        verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+        verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
         verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue()));
 
         // verify that even if timeout is triggered afterwards, SendMgmtFrameCallback is not
@@ -917,8 +917,8 @@
      */
     @Test
     public void testSendMgmtFrameTimeout() throws Exception {
-        WifiCondManager.SendMgmtFrameCallback cb = mock(
-                WifiCondManager.SendMgmtFrameCallback.class);
+        WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+                WifiNl80211Manager.SendMgmtFrameCallback.class);
 
         final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
                 ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -935,7 +935,7 @@
         handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
         mLooper.dispatchAll();
         verify(cb, never()).onAck(anyInt());
-        verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
+        verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
 
         // verify that even if onAck() callback is triggered after timeout,
         // SendMgmtFrameCallback is not triggered again
@@ -1006,7 +1006,8 @@
         sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS);
         mLooper.dispatchAll();
         verify(mSendMgmtFrameCallback, never()).onAck(anyInt());
-        verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
+        verify(mSendMgmtFrameCallback).onFailure(
+                WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
     }
 
     /**
@@ -1032,9 +1033,10 @@
         handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
         // OnFailure posts to the handler
         sendMgmtFrameEventCaptor.getValue().OnFailure(
-                WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+                WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
         mLooper.dispatchAll();
-        verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
+        verify(mSendMgmtFrameCallback).onFailure(
+                WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
     }
 
     /**