Merge "Remane shouldExtendLifetime to meaningful maybeExtendLifetime"
diff --git a/Android.bp b/Android.bp
index d64951c..ab11899 100644
--- a/Android.bp
+++ b/Android.bp
@@ -81,6 +81,7 @@
         // Java/AIDL sources under frameworks/base
         ":framework-annotations",
         ":framework-blobstore-sources",
+        ":framework-bluetooth-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready
         ":framework-connectivity-tiramisu-sources",
         ":framework-core-sources",
         ":framework-drm-sources",
@@ -93,7 +94,7 @@
         ":framework-mca-effect-sources",
         ":framework-mca-filterfw-sources",
         ":framework-mca-filterpacks-sources",
-        ":framework-media-sources",
+        ":framework-media-non-updatable-sources",
         ":framework-mms-sources",
         ":framework-omapi-sources",
         ":framework-opengl-sources",
@@ -155,38 +156,6 @@
 }
 
 java_library_with_nonpublic_deps {
-    name: "framework-updatable-stubs-module_libs_api",
-    static_libs: [
-        "android.net.ipsec.ike.stubs.module_lib",
-        "framework-appsearch.stubs.module_lib",
-        "framework-connectivity.stubs.module_lib",
-        "framework-connectivity-tiramisu.stubs.module_lib",
-        "framework-graphics.stubs.module_lib",
-        "framework-media.stubs.module_lib",
-        "framework-mediaprovider.stubs.module_lib",
-        "framework-permission.stubs.module_lib",
-        "framework-permission-s.stubs.module_lib",
-        "framework-scheduling.stubs.module_lib",
-        "framework-sdkextensions.stubs.module_lib",
-        "framework-statsd.stubs.module_lib",
-        "framework-supplementalprocess.stubs.module_lib",
-        "framework-tethering.stubs.module_lib",
-        "framework-uwb.stubs.module_lib",
-        "framework-nearby.stubs.module_lib",
-        "framework-wifi.stubs.module_lib",
-    ],
-    soong_config_variables: {
-        include_nonpublic_framework_api: {
-            static_libs: [
-                "framework-supplementalapi.stubs.module_lib",
-            ],
-        },
-    },
-    sdk_version: "module_current",
-    visibility: ["//visibility:private"],
-}
-
-java_library_with_nonpublic_deps {
     name: "framework-all",
     installable: false,
     static_libs: [
@@ -212,7 +181,7 @@
     soong_config_variables: {
         include_nonpublic_framework_api: {
             static_libs: [
-                "framework-supplementalapi.stubs.module_lib",
+                "framework-supplementalapi.impl",
             ],
         },
     },
@@ -466,7 +435,6 @@
     name: "framework-ike-shared-srcs",
     visibility: ["//packages/modules/IPsec"],
     srcs: [
-        "core/java/android/net/annotations/PolicyDirection.java",
         "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 7d4a5e5..4aecc8f 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -60,9 +60,9 @@
     defaults: ["android-non-updatable-stubs-defaults"],
     srcs: [
         // No longer part of the stubs, but are included in the docs.
-        "test-base/src/**/*.java",
-        "test-mock/src/**/*.java",
-        "test-runner/src/**/*.java",
+        ":android-test-base-sources",
+        ":android-test-mock-sources",
+        ":android-test-runner-sources",
     ],
     libs: framework_docs_only_libs,
     create_doc_stubs: true,
@@ -74,11 +74,6 @@
     srcs: [
         ":android-non-updatable-stub-sources",
 
-        // Module sources
-        ":art.module.public.api{.public.stubs.source}",
-        ":conscrypt.module.public.api{.public.stubs.source}",
-        ":i18n.module.public.api{.public.stubs.source}",
-
         // No longer part of the stubs, but are included in the docs.
         ":android-test-base-sources",
         ":android-test-mock-sources",
@@ -116,6 +111,10 @@
     name: "framework-doc-stubs-sources-default",
     defaults: ["framework-doc-stubs-default"],
     srcs: [
+        ":art.module.public.api{.public.stubs.source}",
+        ":conscrypt.module.public.api{.public.stubs.source}",
+        ":i18n.module.public.api{.public.stubs.source}",
+
         ":framework-appsearch-sources",
         ":framework-connectivity-sources",
         ":framework-connectivity-tiramisu-updatable-sources",
@@ -160,26 +159,8 @@
 droidstubs {
     name: "framework-doc-stubs",
     defaults: ["framework-doc-stubs-default"],
+    srcs: [":all-modules-public-stubs-source"],
     args: metalava_framework_docs_args,
-    srcs: [
-        ":android.net.ipsec.ike{.public.stubs.source}",
-        ":framework-appsearch{.public.stubs.source}",
-        ":framework-connectivity{.public.stubs.source}",
-        ":framework-connectivity-tiramisu{.public.stubs.source}",
-        ":framework-graphics{.public.stubs.source}",
-        ":framework-media{.public.stubs.source}",
-        ":framework-mediaprovider{.public.stubs.source}",
-        ":framework-nearby{.public.stubs.source}",
-        ":framework-permission{.public.stubs.source}",
-        ":framework-permission-s{.public.stubs.source}",
-        ":framework-scheduling{.public.stubs.source}",
-        ":framework-sdkextensions{.public.stubs.source}",
-        ":framework-statsd{.public.stubs.source}",
-        ":framework-supplementalprocess{.public.stubs.source}",
-        ":framework-tethering{.public.stubs.source}",
-        ":framework-uwb{.public.stubs.source}",
-        ":framework-wifi{.public.stubs.source}",
-    ],
     aidl: {
         local_include_dirs: [
             "apex/media/aidl/stable",
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index a1a46af..9fb1227 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -235,7 +235,6 @@
     public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
     /**
      * All Bluetooth broadcasts.
-     * @hide
      */
     public static final int REASON_BLUETOOTH_BROADCAST = 203;
     /**
@@ -259,6 +258,12 @@
      * @hide
      */
     public static final int REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED = 207;
+    /**
+     * Broadcast {@link android.content.Intent#ACTION_REFRESH_SAFETY_SOURCES}.
+     * @hide
+     */
+    public static final int REASON_ACTION_REFRESH_SAFETY_SOURCES = 208;
+
     /* Reason code range 300-399 are reserved for other internal reasons */
     /**
      * Device idle system allow list, including EXCEPT-IDLE
@@ -399,6 +404,7 @@
             REASON_TIME_CHANGED,
             REASON_LOCALE_CHANGED,
             REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
+            REASON_ACTION_REFRESH_SAFETY_SOURCES,
             REASON_SYSTEM_ALLOW_LISTED,
             REASON_ALARM_MANAGER_ALARM_CLOCK,
             REASON_ALARM_MANAGER_WHILE_IDLE,
@@ -682,6 +688,8 @@
                 return "LOCALE_CHANGED";
             case REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
                 return "REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED";
+            case REASON_ACTION_REFRESH_SAFETY_SOURCES:
+                return "REASON_ACTION_REFRESH_SAFETY_SOURCES";
             case REASON_SYSTEM_ALLOW_LISTED:
                 return "SYSTEM_ALLOW_LISTED";
             case REASON_ALARM_MANAGER_ALARM_CLOCK:
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 3fb1fad..12a8654 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1556,7 +1556,7 @@
                     Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias);
                 }
                 for (int c = 0; c < mControllers.size(); ++c) {
-                    mControllers.get(c).onUidBiasChangedLocked(uid, newBias);
+                    mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
                 }
             }
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index f733849..63781ae 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -18,6 +18,14 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
@@ -27,6 +35,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
 
@@ -42,10 +51,26 @@
     private static final boolean DEBUG = JobSchedulerService.DEBUG
             || Log.isLoggable(TAG, Log.DEBUG);
 
+    @GuardedBy("mLock")
     private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
+    /**
+     * List of jobs that started while the UID was in the TOP state.
+     */
+    @GuardedBy("mLock")
+    private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
+
+    private final PowerTracker mPowerTracker;
+
+    /**
+     * Helper set to avoid too much GC churn from frequent calls to
+     * {@link #maybeReportNewChargingStateLocked()}.
+     */
+    private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
 
     public BatteryController(JobSchedulerService service) {
         super(service);
+        mPowerTracker = new PowerTracker();
+        mPowerTracker.startTracking();
     }
 
     @Override
@@ -54,8 +79,15 @@
             final long nowElapsed = sElapsedRealtimeClock.millis();
             mTrackedTasks.add(taskStatus);
             taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
-            taskStatus.setChargingConstraintSatisfied(nowElapsed,
-                    mService.isBatteryCharging() && mService.isBatteryNotLow());
+            if (taskStatus.hasChargingConstraint()) {
+                if (hasTopExemptionLocked(taskStatus)) {
+                    taskStatus.setChargingConstraintSatisfied(nowElapsed,
+                            mPowerTracker.isPowerConnected());
+                } else {
+                    taskStatus.setChargingConstraintSatisfied(nowElapsed,
+                            mService.isBatteryCharging() && mService.isBatteryNotLow());
+                }
+            }
             taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow());
         }
     }
@@ -66,9 +98,32 @@
     }
 
     @Override
+    @GuardedBy("mLock")
+    public void prepareForExecutionLocked(JobStatus jobStatus) {
+        if (DEBUG) {
+            Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+        }
+
+        final int uid = jobStatus.getSourceUid();
+        if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
+            if (DEBUG) {
+                Slog.d(TAG, jobStatus.toShortString() + " is top started job");
+            }
+            mTopStartedJobs.add(jobStatus);
+        }
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+        mTopStartedJobs.remove(jobStatus);
+    }
+
+    @Override
     public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
         if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
             mTrackedTasks.remove(taskStatus);
+            mTopStartedJobs.remove(taskStatus);
         }
     }
 
@@ -90,33 +145,124 @@
         });
     }
 
+    @Override
+    @GuardedBy("mLock")
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
+        if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) {
+            maybeReportNewChargingStateLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) {
+        return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
+                || mTopStartedJobs.contains(taskStatus);
+    }
+
     @GuardedBy("mLock")
     private void maybeReportNewChargingStateLocked() {
+        final boolean powerConnected = mPowerTracker.isPowerConnected();
         final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow();
         final boolean batteryNotLow = mService.isBatteryNotLow();
         if (DEBUG) {
-            Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
+            Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+                    + powerConnected + "/" + stablePower + "/" + batteryNotLow);
         }
         final long nowElapsed = sElapsedRealtimeClock.millis();
-        boolean reportChange = false;
         for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
             final JobStatus ts = mTrackedTasks.valueAt(i);
-            reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
-            reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
+            if (ts.hasChargingConstraint()) {
+                if (hasTopExemptionLocked(ts)
+                        && ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
+                    // If the job started while the app was on top or the app is currently on top,
+                    // let the job run as long as there's power connected, even if the device isn't
+                    // officially charging.
+                    // For user requested/initiated jobs, users may be confused when the task stops
+                    // running even though the device is plugged in.
+                    // Low priority jobs don't need to be exempted.
+                    if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) {
+                        mChangedJobs.add(ts);
+                    }
+                } else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) {
+                    mChangedJobs.add(ts);
+                }
+            }
+            if (ts.hasBatteryNotLowConstraint()
+                    && ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) {
+                mChangedJobs.add(ts);
+            }
         }
         if (stablePower || batteryNotLow) {
             // If one of our conditions has been satisfied, always schedule any newly ready jobs.
             mStateChangedListener.onRunJobNow(null);
-        } else if (reportChange) {
+        } else if (mChangedJobs.size() > 0) {
             // Otherwise, just let the job scheduler know the state has changed and take care of it
             // as it thinks is best.
-            mStateChangedListener.onControllerStateChanged(mTrackedTasks);
+            mStateChangedListener.onControllerStateChanged(mChangedJobs);
+        }
+        mChangedJobs.clear();
+    }
+
+    private final class PowerTracker extends BroadcastReceiver {
+        /**
+         * Track whether there is power connected. It doesn't mean the device is charging.
+         * Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is
+         * charging.
+         */
+        private boolean mPowerConnected;
+
+        PowerTracker() {
+        }
+
+        void startTracking() {
+            IntentFilter filter = new IntentFilter();
+
+            filter.addAction(Intent.ACTION_POWER_CONNECTED);
+            filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+            mContext.registerReceiver(this, filter);
+
+            // Initialize tracker state.
+            BatteryManagerInternal batteryManagerInternal =
+                    LocalServices.getService(BatteryManagerInternal.class);
+            mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+        }
+
+        boolean isPowerConnected() {
+            return mPowerConnected;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                final String action = intent.getAction();
+
+                if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis());
+                    }
+                    if (mPowerConnected) {
+                        return;
+                    }
+                    mPowerConnected = true;
+                } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis());
+                    }
+                    if (!mPowerConnected) {
+                        return;
+                    }
+                    mPowerConnected = false;
+                }
+
+                maybeReportNewChargingStateLocked();
+            }
         }
     }
 
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
+        pw.println("Power connected: " + mPowerTracker.isPowerConnected());
         pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow()));
         pw.println("Not low: " + mService.isBatteryNotLow());
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 9fb7ab5..c678755 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -517,7 +517,7 @@
 
     @GuardedBy("mLock")
     @Override
-    public void onUidBiasChangedLocked(int uid, int newBias) {
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
         UidStats uidStats = mUidStats.get(uid);
         if (uidStats != null && uidStats.baseBias != newBias) {
             uidStats.baseBias = newBias;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 9749c80..428f2cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -40,7 +40,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArrayMap;
-import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
@@ -81,9 +80,6 @@
      */
     @GuardedBy("mLock")
     private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>();
-    /** Cached list of UIDs in the TOP state. */
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mTopUids = new SparseBooleanArray();
     private final ThresholdAlarmListener mThresholdAlarmListener;
 
     /**
@@ -186,15 +182,9 @@
 
     @GuardedBy("mLock")
     @Override
-    public void onUidBiasChangedLocked(int uid, int newBias) {
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
         final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP;
-        final boolean wasTop = mTopUids.get(uid);
-        if (isNowTop) {
-            mTopUids.put(uid, true);
-        } else {
-            // Delete entries of non-top apps so the set doesn't get too large.
-            mTopUids.delete(uid);
-        }
+        final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP;
         if (isNowTop != wasTop) {
             mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget();
         }
@@ -314,7 +304,8 @@
         //   3. The app is not open but has an active widget (we can't tell if a widget displays
         //      status/data, so this assumes the prefetch job is to update the data displayed on
         //      the widget).
-        final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid());
+        final boolean appIsOpen =
+                mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP;
         final boolean satisfied;
         if (!appIsOpen) {
             final int userId = jobStatus.getSourceUserId();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 509fb69..2a2d602 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -144,7 +144,7 @@
      * important the UID is.
      */
     @GuardedBy("mLock")
-    public void onUidBiasChangedLocked(int uid, int newBias) {
+    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
     }
 
     protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
diff --git a/api/Android.bp b/api/Android.bp
index 7aa68f8..362f39f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -32,6 +32,7 @@
         "soong",
         "soong-android",
         "soong-genrule",
+        "soong-java",
     ],
     srcs: ["api.go"],
     pluginFor: ["soong_build"],
diff --git a/api/api.go b/api/api.go
index 6cc129a..4b6ebc1 100644
--- a/api/api.go
+++ b/api/api.go
@@ -21,8 +21,13 @@
 
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/java"
 )
 
+const art = "art.module.public.api"
+const conscrypt = "conscrypt.module.public.api"
+const i18n = "i18n.module.public.api"
+
 // The intention behind this soong plugin is to generate a number of "merged"
 // API-related modules that would otherwise require a large amount of very
 // similar Android.bp boilerplate to define. For example, the merged current.txt
@@ -69,6 +74,19 @@
 	Visibility []string
 }
 
+type libraryProps struct {
+	Name        *string
+	Sdk_version *string
+	Static_libs []string
+	Visibility  []string
+}
+
+type fgProps struct {
+	Name       *string
+	Srcs       []string
+	Visibility []string
+}
+
 // Struct to pass parameters for the various merged [current|removed].txt file modules we create.
 type MergedTxtDefinition struct {
 	// "current.txt" or "removed.txt"
@@ -99,7 +117,7 @@
 	props.Tools = []string{"metalava"}
 	props.Out = []string{filename}
 	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
-	props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag)
+	props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
 	props.Dists = []android.Dist{
 		{
 			Targets: []string{"droidcore"},
@@ -122,7 +140,7 @@
 	props.Tools = []string{"merge_zips"}
 	props.Out = []string{"current.srcjar"}
 	props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
-	props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}")
+	props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...)
 	props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
 }
@@ -130,17 +148,28 @@
 // This produces the same annotations.zip as framework-doc-stubs, but by using
 // outputs from individual modules instead of all the source code.
 func createMergedAnnotations(ctx android.LoadHookContext, modules []string) {
+	// Conscrypt and i18n currently do not enable annotations
+	modules = removeAll(modules, []string{conscrypt, i18n})
 	props := genruleProps{}
 	props.Name = proptools.StringPtr("sdk-annotations.zip")
 	props.Tools = []string{"merge_annotation_zips", "soong_zip"}
 	props.Out = []string{"annotations.zip"}
 	props.Cmd = proptools.StringPtr("$(location merge_annotation_zips) $(genDir)/out $(in) && " +
 		"$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out")
-	props.Srcs = createSrcs(":android-non-updatable-doc-stubs{.annotations.zip}", modules, "{.public.annotations.zip}")
+	props.Srcs = append([]string{":android-non-updatable-doc-stubs{.annotations.zip}"}, createSrcs(modules, "{.public.annotations.zip}")...)
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
 }
 
 func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
+	// For the filtered api versions, we prune all APIs except art module's APIs. because
+	// 1) ART apis are available by default to all modules, while other module-to-module deps are
+	//    explicit and probably receive more scrutiny anyway
+	// 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
+	// 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
+	//    per-module lint databases that excludes just that module's APIs. Alas, that's more
+	//    difficult to achieve.
+	modules = remove(modules, art)
+
 	props := genruleProps{}
 	props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
 	props.Tools = []string{"api_versions_trimmer"}
@@ -149,36 +178,34 @@
 	// Note: order matters: first parameter is the full api-versions.xml
 	// after that the stubs files in any order
 	// stubs files are all modules that export API surfaces EXCEPT ART
-	props.Srcs = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}")
+	props.Srcs = append([]string{":framework-doc-stubs{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...)
 	props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
 }
 
-func createSrcs(base string, modules []string, tag string) []string {
-	a := make([]string, 0, len(modules)+1)
-	a = append(a, base)
-	for _, module := range modules {
-		a = append(a, ":"+module+tag)
-	}
-	return a
+func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+	// The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+	modules = removeAll(modules, []string{art, conscrypt, i18n})
+	props := libraryProps{}
+	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
+	props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
+	props.Sdk_version = proptools.StringPtr("module_current")
+	props.Visibility = []string{"//frameworks/base"}
+	ctx.CreateModule(java.LibraryFactory, &props)
 }
 
-func remove(s []string, v string) []string {
-	s2 := make([]string, 0, len(s))
-	for _, sv := range s {
-		if sv != v {
-			s2 = append(s2, sv)
-		}
-	}
-	return s2
+func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) {
+	props := fgProps{}
+	props.Name = proptools.StringPtr("all-modules-public-stubs-source")
+	props.Srcs = createSrcs(modules, "{.public.stubs.source}")
+	props.Visibility = []string{"//frameworks/base"}
+	ctx.CreateModule(android.FileGroupFactory, &props)
 }
 
 func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
 	var textFiles []MergedTxtDefinition
 	// Two module libraries currently do not support @SystemApi so only have the public scope.
-	bcpWithSystemApi := bootclasspath
-	bcpWithSystemApi = remove(bcpWithSystemApi, "conscrypt.module.public.api")
-	bcpWithSystemApi = remove(bcpWithSystemApi, "i18n.module.public.api")
+	bcpWithSystemApi := removeAll(bootclasspath, []string{conscrypt, i18n})
 
 	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
 	for i, f := range []string{"current.txt", "removed.txt"} {
@@ -226,22 +253,13 @@
 
 	createMergedStubsSrcjar(ctx, bootclasspath)
 
-	// Conscrypt and i18n currently do not enable annotations
-	annotationModules := bootclasspath
-	annotationModules = remove(annotationModules, "conscrypt.module.public.api")
-	annotationModules = remove(annotationModules, "i18n.module.public.api")
-	createMergedAnnotations(ctx, annotationModules)
+	createMergedModuleLibStubs(ctx, bootclasspath)
 
-	// For the filtered api versions, we prune all APIs except art module's APIs. because
-	// 1) ART apis are available by default to all modules, while other module-to-module deps are
-	//    explicit and probably receive more scrutiny anyway
-	// 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
-	// 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
-	//    per-module lint databases that excludes just that module's APIs. Alas, that's more
-	//    difficult to achieve.
-	filteredModules := bootclasspath
-	filteredModules = remove(filteredModules, "art.module.public.api")
-	createFilteredApiVersions(ctx, filteredModules)
+	createMergedAnnotations(ctx, bootclasspath)
+
+	createFilteredApiVersions(ctx, bootclasspath)
+
+	createPublicStubsSourceFilegroup(ctx, bootclasspath)
 }
 
 func combinedApisModuleFactory() android.Module {
@@ -251,3 +269,36 @@
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
 	return module
 }
+
+// Various utility methods below.
+
+// Creates an array of ":<m><tag>" for each m in <modules>.
+func createSrcs(modules []string, tag string) []string {
+	return transformArray(modules, ":", tag)
+}
+
+// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
+func transformArray(modules []string, prefix, suffix string) []string {
+	a := make([]string, 0, len(modules))
+	for _, module := range modules {
+		a = append(a, prefix+module+suffix)
+	}
+	return a
+}
+
+func removeAll(s []string, vs []string) []string {
+	for _, v := range vs {
+		s = remove(s, v)
+	}
+	return s
+}
+
+func remove(s []string, v string) []string {
+	s2 := make([]string, 0, len(s))
+	for _, sv := range s {
+		if sv != v {
+			s2 = append(s2, sv)
+		}
+	}
+	return s2
+}
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 260c8a4..b384e70 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -259,7 +259,8 @@
     public void runDisableAppDataIsolation() throws RemoteException {
         if (!SystemProperties.getBoolean(
                 ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
-            throw new IllegalStateException("Storage app data isolation is not enabled.");
+            System.err.println("Storage app data isolation is not enabled.");
+            return;
         }
         final String pkgName = nextArg();
         final int pid = Integer.parseInt(nextArg());
diff --git a/core/api/current.txt b/core/api/current.txt
index 4f15fa9..280eb80 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17,6 +17,7 @@
     field public static final String ACCESS_MEDIA_LOCATION = "android.permission.ACCESS_MEDIA_LOCATION";
     field public static final String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
     field public static final String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
+    field public static final String ACCESS_SUPPLEMENTAL_APIS = "android.permission.ACCESS_SUPPLEMENTAL_APIS";
     field public static final String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
     field public static final String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
     field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
@@ -971,6 +972,7 @@
     field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
     field public static final int listViewStyle = 16842868; // 0x1010074
     field public static final int listViewWhiteStyle = 16842869; // 0x1010075
+    field public static final int localeConfig;
     field public static final int lockTaskMode = 16844013; // 0x10104ed
     field public static final int logo = 16843454; // 0x10102be
     field public static final int logoDescription = 16844009; // 0x10104e9
@@ -1312,6 +1314,7 @@
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c
     field public static final int showAsAction = 16843481; // 0x10102d9
+    field public static final int showBackground;
     field public static final int showClockAndComplications;
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
@@ -5696,6 +5699,17 @@
     method @Deprecated public android.view.Window startActivity(String, android.content.Intent);
   }
 
+  public class LocaleConfig {
+    ctor public LocaleConfig(@NonNull android.content.Context);
+    method public int getStatus();
+    method @Nullable public android.os.LocaleList getSupportedLocales();
+    field public static final int STATUS_NOT_SPECIFIED = 1; // 0x1
+    field public static final int STATUS_PARSING_FAILED = 2; // 0x2
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final String TAG_LOCALE = "locale";
+    field public static final String TAG_LOCALE_CONFIG = "locale-config";
+  }
+
   public class LocaleManager {
     method @NonNull public android.os.LocaleList getApplicationLocales();
     method public void setApplicationLocales(@NonNull android.os.LocaleList);
@@ -7273,6 +7287,10 @@
     method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
     method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
     method public CharSequence getDeviceOwnerLockScreenInfo();
+    method @Nullable public android.graphics.drawable.Drawable getDrawable(int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @Nullable public android.graphics.drawable.Drawable getDrawable(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+    method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
     method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
     method @NonNull public String getEnrollmentSpecificId();
     method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
@@ -7496,6 +7514,7 @@
     field public static final String ACTION_CHECK_POLICY_COMPLIANCE = "android.app.action.CHECK_POLICY_COMPLIANCE";
     field public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
     field public static final String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
+    field public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED = "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED";
     field public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE";
     field public static final String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED";
@@ -7571,6 +7590,8 @@
     field public static final String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
     field public static final String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
     field public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
+    field public static final String EXTRA_RESOURCE_ID = "android.app.extra.RESOURCE_ID";
+    field public static final String EXTRA_RESOURCE_TYPE_DRAWABLE = "android.app.extra.RESOURCE_TYPE_DRAWABLE";
     field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
     field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
     field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -7665,6 +7686,35 @@
     method public void onApplicationUserDataCleared(String, boolean);
   }
 
+  public final class DevicePolicyResources {
+    ctor public DevicePolicyResources();
+  }
+
+  public static final class DevicePolicyResources.Drawable {
+    field public static final int INVALID_ID = -1; // 0xffffffff
+    field public static final int WORK_PROFILE_ICON = 1; // 0x1
+    field public static final int WORK_PROFILE_ICON_BADGE = 0; // 0x0
+    field public static final int WORK_PROFILE_OFF_ICON = 2; // 0x2
+    field public static final int WORK_PROFILE_USER_ICON = 3; // 0x3
+  }
+
+  public static final class DevicePolicyResources.Drawable.Source {
+    field public static final int HOME_WIDGET = 2; // 0x2
+    field public static final int LAUNCHER_OFF_BUTTON = 3; // 0x3
+    field public static final int NOTIFICATION = 0; // 0x0
+    field public static final int PROFILE_SWITCH_ANIMATION = 1; // 0x1
+    field public static final int QUICK_SETTINGS = 4; // 0x4
+    field public static final int STATUS_BAR = 5; // 0x5
+    field public static final int UNDEFINED = -1; // 0xffffffff
+  }
+
+  public static final class DevicePolicyResources.Drawable.Style {
+    field public static final int DEFAULT = -1; // 0xffffffff
+    field public static final int OUTLINE = 2; // 0x2
+    field public static final int SOLID_COLORED = 0; // 0x0
+    field public static final int SOLID_NOT_COLORED = 1; // 0x1
+  }
+
   public final class DnsEvent extends android.app.admin.NetworkEvent implements android.os.Parcelable {
     method public String getHostname();
     method public java.util.List<java.net.InetAddress> getInetAddresses();
@@ -10773,7 +10823,7 @@
     method public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
     method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
     method public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
-    method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL", android.Manifest.permission.INTERACT_ACROSS_PROFILES}, conditional=true) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
     method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
     method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
     method @NonNull public int[] checkCallingOrSelfUriPermissions(@NonNull java.util.List<android.net.Uri>, int);
@@ -15229,6 +15279,11 @@
 
   public class BitmapShader extends android.graphics.Shader {
     ctor public BitmapShader(@NonNull android.graphics.Bitmap, @NonNull android.graphics.Shader.TileMode, @NonNull android.graphics.Shader.TileMode);
+    method public int getFilterMode();
+    method public void setFilterMode(int);
+    field public static final int FILTER_MODE_DEFAULT = 0; // 0x0
+    field public static final int FILTER_MODE_LINEAR = 2; // 0x2
+    field public static final int FILTER_MODE_NEAREST = 1; // 0x1
   }
 
   public enum BlendMode {
@@ -16635,6 +16690,7 @@
     method public void setFloatUniform(@NonNull String, float, float, float);
     method public void setFloatUniform(@NonNull String, float, float, float, float);
     method public void setFloatUniform(@NonNull String, @NonNull float[]);
+    method public void setInputBuffer(@NonNull String, @NonNull android.graphics.BitmapShader);
     method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
     method public void setIntUniform(@NonNull String, int);
     method public void setIntUniform(@NonNull String, int, int);
@@ -18015,6 +18071,7 @@
     field public static final String STRING_TYPE_GRAVITY = "android.sensor.gravity";
     field public static final String STRING_TYPE_GYROSCOPE = "android.sensor.gyroscope";
     field public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = "android.sensor.gyroscope_uncalibrated";
+    field public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
     field public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
     field public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
     field public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
@@ -18045,6 +18102,7 @@
     field public static final int TYPE_GRAVITY = 9; // 0x9
     field public static final int TYPE_GYROSCOPE = 4; // 0x4
     field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
+    field public static final int TYPE_HEAD_TRACKER = 37; // 0x25
     field public static final int TYPE_HEART_BEAT = 31; // 0x1f
     field public static final int TYPE_HEART_RATE = 21; // 0x15
     field public static final int TYPE_HINGE_ANGLE = 36; // 0x24
@@ -18107,6 +18165,7 @@
     method public void onFlushCompleted(android.hardware.Sensor);
     method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
     method public void onSensorChanged(android.hardware.SensorEvent);
+    method public void onSensorDiscontinuity(@NonNull android.hardware.Sensor);
   }
 
   public interface SensorEventListener {
@@ -18330,10 +18389,12 @@
     ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
-    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+    ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
     method public javax.crypto.Cipher getCipher();
-    method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+    method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method public javax.crypto.Mac getMac();
+    method @Nullable public android.security.identity.PresentationSession getPresentationSession();
     method public java.security.Signature getSignature();
   }
 
@@ -20149,6 +20210,24 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SphericalCorrections> CREATOR;
   }
 
+  public final class GnssAutomaticGainControl implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0) public long getCarrierFrequencyHz();
+    method public int getConstellationType();
+    method @FloatRange(from=0xffffd8f0, to=10000) public double getLevelDb();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAutomaticGainControl> CREATOR;
+  }
+
+  public static final class GnssAutomaticGainControl.Builder {
+    ctor public GnssAutomaticGainControl.Builder();
+    ctor public GnssAutomaticGainControl.Builder(@NonNull android.location.GnssAutomaticGainControl);
+    method @NonNull public android.location.GnssAutomaticGainControl build();
+    method @NonNull public android.location.GnssAutomaticGainControl.Builder setCarrierFrequencyHz(@IntRange(from=0) long);
+    method @NonNull public android.location.GnssAutomaticGainControl.Builder setConstellationType(int);
+    method @NonNull public android.location.GnssAutomaticGainControl.Builder setLevelDb(@FloatRange(from=0xffffd8f0, to=10000) double);
+  }
+
   public final class GnssCapabilities implements android.os.Parcelable {
     method public int describeContents();
     method public boolean hasAntennaInfo();
@@ -20205,7 +20284,7 @@
     method public double getAccumulatedDeltaRangeMeters();
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
-    method public double getAutomaticGainControlLevelDb();
+    method @Deprecated public double getAutomaticGainControlLevelDb();
     method @FloatRange(from=0, to=63) public double getBasebandCn0DbHz();
     method @Deprecated public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
@@ -20227,7 +20306,7 @@
     method public int getState();
     method public int getSvid();
     method public double getTimeOffsetNanos();
-    method public boolean hasAutomaticGainControlLevelDb();
+    method @Deprecated public boolean hasAutomaticGainControlLevelDb();
     method public boolean hasBasebandCn0DbHz();
     method @Deprecated public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
@@ -20289,11 +20368,21 @@
   public final class GnssMeasurementsEvent implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.location.GnssClock getClock();
+    method @NonNull public java.util.Collection<android.location.GnssAutomaticGainControl> getGnssAutomaticGainControls();
     method @NonNull public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
   }
 
+  public static final class GnssMeasurementsEvent.Builder {
+    ctor public GnssMeasurementsEvent.Builder();
+    ctor public GnssMeasurementsEvent.Builder(@NonNull android.location.GnssMeasurementsEvent);
+    method @NonNull public android.location.GnssMeasurementsEvent build();
+    method @NonNull public android.location.GnssMeasurementsEvent.Builder setClock(@NonNull android.location.GnssClock);
+    method @NonNull public android.location.GnssMeasurementsEvent.Builder setGnssAutomaticGainControls(@NonNull java.util.Collection<android.location.GnssAutomaticGainControl>);
+    method @NonNull public android.location.GnssMeasurementsEvent.Builder setMeasurements(@NonNull java.util.Collection<android.location.GnssMeasurement>);
+  }
+
   public abstract static class GnssMeasurementsEvent.Callback {
     ctor public GnssMeasurementsEvent.Callback();
     method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
@@ -20965,6 +21054,7 @@
     method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
     method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public int getAllowedCapturePolicy();
+    method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAudioDevicesForAttributes(@NonNull android.media.AudioAttributes);
     method public int getAudioHwSyncForSession(int);
     method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAvailableCommunicationDevices();
     method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
@@ -27724,6 +27814,8 @@
     ctor public VcnCellUnderlyingNetworkTemplate.Builder();
     method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
     method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
     method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
     method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
     method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
@@ -27784,6 +27876,10 @@
 
   public abstract class VcnUnderlyingNetworkTemplate {
     method public int getMetered();
+    method public int getMinEntryDownstreamBandwidthKbps();
+    method public int getMinEntryUpstreamBandwidthKbps();
+    method public int getMinExitDownstreamBandwidthKbps();
+    method public int getMinExitUpstreamBandwidthKbps();
     field public static final int MATCH_ANY = 0; // 0x0
     field public static final int MATCH_FORBIDDEN = 2; // 0x2
     field public static final int MATCH_REQUIRED = 1; // 0x1
@@ -27797,6 +27893,8 @@
     ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
     method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
     method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
     method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
   }
 
@@ -38029,6 +38127,51 @@
     ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
   }
 
+  public class CredentialDataRequest {
+    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getDeviceSignedEntriesToRequest();
+    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getIssuerSignedEntriesToRequest();
+    method @Nullable public byte[] getReaderSignature();
+    method @Nullable public byte[] getRequestMessage();
+    method public boolean isAllowUsingExhaustedKeys();
+    method public boolean isAllowUsingExpiredKeys();
+    method public boolean isIncrementUseCount();
+  }
+
+  public static final class CredentialDataRequest.Builder {
+    ctor public CredentialDataRequest.Builder();
+    method @NonNull public android.security.identity.CredentialDataRequest build();
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExhaustedKeys(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExpiredKeys(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setDeviceSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIncrementUseCount(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIssuerSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setReaderSignature(@NonNull byte[]);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setRequestMessage(@NonNull byte[]);
+  }
+
+  public abstract class CredentialDataResult {
+    method @Nullable public abstract byte[] getDeviceMac();
+    method @NonNull public abstract byte[] getDeviceNameSpaces();
+    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
+    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
+    method @NonNull public abstract byte[] getStaticAuthenticationData();
+  }
+
+  public static interface CredentialDataResult.Entries {
+    method @Nullable public byte[] getEntry(@NonNull String, @NonNull String);
+    method @NonNull public java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @NonNull public java.util.Collection<java.lang.String> getNamespaces();
+    method @NonNull public java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method public int getStatus(@NonNull String, @NonNull String);
+    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  }
+
   public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
     ctor public DocTypeNotSupportedException(@NonNull String);
     ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -38040,19 +38183,19 @@
   }
 
   public abstract class IdentityCredential {
-    method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
-    method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+    method @Deprecated @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
+    method @Deprecated @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
     method @NonNull public byte[] delete(@NonNull byte[]);
-    method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
+    method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
     method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
     method @NonNull public abstract int[] getAuthenticationDataUsageCount();
     method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
-    method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+    method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
     method @NonNull public byte[] proveOwnership(@NonNull byte[]);
-    method public abstract void setAllowUsingExhaustedKeys(boolean);
-    method public void setAllowUsingExpiredKeys(boolean);
+    method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
+    method @Deprecated public void setAllowUsingExpiredKeys(boolean);
     method public abstract void setAvailableAuthenticationKeys(int, int);
-    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
     method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
     method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
     method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
@@ -38065,6 +38208,7 @@
 
   public abstract class IdentityCredentialStore {
     method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
+    method @NonNull public android.security.identity.PresentationSession createPresentationSession(int) throws android.security.identity.CipherSuiteNotSupportedException;
     method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
     method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
     method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
@@ -38103,22 +38247,29 @@
     method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
   }
 
-  public abstract class ResultData {
-    method @NonNull public abstract byte[] getAuthenticatedData();
-    method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
-    method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
-    method @Nullable public abstract byte[] getMessageAuthenticationCode();
-    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
-    method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
-    method @NonNull public abstract byte[] getStaticAuthenticationData();
-    method public abstract int getStatus(@NonNull String, @NonNull String);
-    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
-    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
-    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
-    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
-    field public static final int STATUS_OK = 0; // 0x0
-    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
-    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  public abstract class PresentationSession {
+    method @Nullable public abstract android.security.identity.CredentialDataResult getCredentialData(@NonNull String, @NonNull android.security.identity.CredentialDataRequest) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException;
+    method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
+    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method public abstract void setSessionTranscript(@NonNull byte[]);
+  }
+
+  @Deprecated public abstract class ResultData {
+    method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
+    method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
+    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
+    method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
+    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
+    method @Deprecated public abstract int getStatus(@NonNull String, @NonNull String);
+    field @Deprecated public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field @Deprecated public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field @Deprecated public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field @Deprecated public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field @Deprecated public static final int STATUS_OK = 0; // 0x0
+    field @Deprecated public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field @Deprecated public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
   }
 
   public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
@@ -47166,6 +47317,15 @@
     field public float ydpi;
   }
 
+  public interface Dumpable {
+    method public void dump(@NonNull java.io.PrintWriter, @Nullable String[]);
+    method @NonNull public default String getDumpableName();
+  }
+
+  public interface DumpableContainer {
+    method public boolean addDumpable(@NonNull android.util.Dumpable);
+  }
+
   public class EventLog {
     method public static int getTagCode(String);
     method public static String getTagName(int);
@@ -47919,15 +48079,33 @@
 
   public final class Choreographer {
     method public static android.view.Choreographer getInstance();
+    method public void postExtendedFrameCallback(@NonNull android.view.Choreographer.ExtendedFrameCallback);
     method public void postFrameCallback(android.view.Choreographer.FrameCallback);
     method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long);
+    method public void removeExtendedFrameCallback(@Nullable android.view.Choreographer.ExtendedFrameCallback);
     method public void removeFrameCallback(android.view.Choreographer.FrameCallback);
   }
 
+  public static interface Choreographer.ExtendedFrameCallback {
+    method public void onVsync(@NonNull android.view.Choreographer.FrameData);
+  }
+
   public static interface Choreographer.FrameCallback {
     method public void doFrame(long);
   }
 
+  public static class Choreographer.FrameData {
+    method public long getFrameTimeNanos();
+    method @NonNull public android.view.Choreographer.FrameTimeline[] getFrameTimelines();
+    method @NonNull public android.view.Choreographer.FrameTimeline getPreferredFrameTimeline();
+  }
+
+  public static class Choreographer.FrameTimeline {
+    method public long getDeadlineNanos();
+    method public long getExpectedPresentTimeNanos();
+    method public long getVsyncId();
+  }
+
   public interface CollapsibleActionView {
     method public void onActionViewCollapsed();
     method public void onActionViewExpanded();
@@ -48073,6 +48251,18 @@
     method @NonNull public android.graphics.Insets getWaterfallInsets();
   }
 
+  public static final class DisplayCutout.Builder {
+    ctor public DisplayCutout.Builder();
+    method @NonNull public android.view.DisplayCutout build();
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectBottom(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectLeft(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectRight(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setBoundingRectTop(@NonNull android.graphics.Rect);
+    method @NonNull public android.view.DisplayCutout.Builder setCutoutPath(@NonNull android.graphics.Path);
+    method @NonNull public android.view.DisplayCutout.Builder setSafeInsets(@NonNull android.graphics.Insets);
+    method @NonNull public android.view.DisplayCutout.Builder setWaterfallInsets(@NonNull android.graphics.Insets);
+  }
+
   public final class DragAndDropPermissions implements android.os.Parcelable {
     method public int describeContents();
     method public void release();
@@ -49165,6 +49355,7 @@
     field public static final int EDGE_LEFT = 4; // 0x4
     field public static final int EDGE_RIGHT = 8; // 0x8
     field public static final int EDGE_TOP = 1; // 0x1
+    field public static final int FLAG_CANCELED = 32; // 0x20
     field public static final int FLAG_WINDOW_IS_OBSCURED = 1; // 0x1
     field public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 2; // 0x2
     field public static final int INVALID_POINTER_ID = -1; // 0xffffffff
@@ -52267,6 +52458,7 @@
     method public final float getFontScale();
     method @Nullable public final java.util.Locale getLocale();
     method @NonNull public android.view.accessibility.CaptioningManager.CaptionStyle getUserStyle();
+    method public boolean isCallCaptioningEnabled();
     method public final boolean isEnabled();
     method public void removeCaptioningChangeListener(@NonNull android.view.accessibility.CaptioningManager.CaptioningChangeListener);
   }
@@ -52338,6 +52530,7 @@
     method public int getRepeatCount();
     method public int getRepeatMode();
     method protected float getScaleFactor();
+    method public boolean getShowBackground();
     method public long getStartOffset();
     method public long getStartTime();
     method public boolean getTransformation(long, android.view.animation.Transformation);
@@ -52363,6 +52556,7 @@
     method public void setInterpolator(android.view.animation.Interpolator);
     method public void setRepeatCount(int);
     method public void setRepeatMode(int);
+    method public void setShowBackground(boolean);
     method public void setStartOffset(long);
     method public void setStartTime(long);
     method public void setZAdjustment(int);
@@ -52868,6 +53062,7 @@
     method public int getCharacterBoundsFlags(int);
     method public CharSequence getComposingText();
     method public int getComposingTextStart();
+    method @Nullable public android.view.inputmethod.EditorBoundsInfo getEditorBoundsInfo();
     method public float getInsertionMarkerBaseline();
     method public float getInsertionMarkerBottom();
     method public int getInsertionMarkerFlags();
@@ -52889,11 +53084,27 @@
     method public android.view.inputmethod.CursorAnchorInfo build();
     method public void reset();
     method public android.view.inputmethod.CursorAnchorInfo.Builder setComposingText(int, CharSequence);
+    method @NonNull public android.view.inputmethod.CursorAnchorInfo.Builder setEditorBoundsInfo(@Nullable android.view.inputmethod.EditorBoundsInfo);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float, int);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setMatrix(android.graphics.Matrix);
     method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
   }
 
+  public final class EditorBoundsInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.graphics.RectF getEditorBounds();
+    method @Nullable public android.graphics.RectF getHandwritingBounds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorBoundsInfo> CREATOR;
+  }
+
+  public static final class EditorBoundsInfo.Builder {
+    ctor public EditorBoundsInfo.Builder();
+    method @NonNull public android.view.inputmethod.EditorBoundsInfo build();
+    method @NonNull public android.view.inputmethod.EditorBoundsInfo.Builder setEditorBounds(@Nullable android.graphics.RectF);
+    method @NonNull public android.view.inputmethod.EditorBoundsInfo.Builder setHandwritingBounds(@Nullable android.graphics.RectF);
+  }
+
   public class EditorInfo implements android.text.InputType android.os.Parcelable {
     ctor public EditorInfo();
     method public int describeContents();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 4d84537..c4aff2d0 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -9,6 +9,10 @@
 
 package android.app {
 
+  @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+    method public final boolean addDumpable(@NonNull android.util.Dumpable);
+  }
+
   public class ActivityManager {
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
     method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
@@ -160,6 +164,7 @@
 
   public final class BtProfileConnectionInfo implements android.os.Parcelable {
     method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
+    method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
     method public int describeContents();
     method public boolean getIsLeOutput();
     method public int getProfile();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1373ccf..800c8ab0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -179,6 +179,7 @@
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
+    field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
     field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
     field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
     field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
@@ -366,7 +367,6 @@
     field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
     field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
     field public static final int config_showDefaultHome = 17891331; // 0x1110003
-    field public static final int config_systemCaptionsServiceCallsEnabled;
   }
 
   public static final class R.color {
@@ -771,18 +771,32 @@
   }
 
   public class KeyguardManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public long addWeakEscrowToken(@NonNull byte[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenActivatedListener);
     method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed();
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean isValidLockPasswordComplexity(int, @NonNull byte[], int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean isWeakEscrowTokenActive(long, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean isWeakEscrowTokenValid(long, @NonNull byte[], @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean registerWeakEscrowTokenRemovedListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean removeWeakEscrowToken(long, @NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean unregisterWeakEscrowTokenRemovedListener(@NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
     field public static final int PASSWORD = 0; // 0x0
     field public static final int PATTERN = 2; // 0x2
     field public static final int PIN = 1; // 0x1
   }
 
+  public static interface KeyguardManager.WeakEscrowTokenActivatedListener {
+    method public void onWeakEscrowTokenActivated(long, @NonNull android.os.UserHandle);
+  }
+
+  public static interface KeyguardManager.WeakEscrowTokenRemovedListener {
+    method public void onWeakEscrowTokenRemoved(long, @NonNull android.os.UserHandle);
+  }
+
   public class LocaleManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_APP_SPECIFIC_LOCALES) public android.os.LocaleList getApplicationLocales(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void setApplicationLocales(@NonNull String, @NonNull android.os.LocaleList);
@@ -979,6 +993,18 @@
 
 package android.app.admin {
 
+  public final class DevicePolicyDrawableResource implements android.os.Parcelable {
+    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, int, int, int, @DrawableRes int);
+    ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, int, int, @DrawableRes int);
+    method public int describeContents();
+    method @DrawableRes public int getCallingPackageResourceId();
+    method public int getDrawableId();
+    method public int getDrawableSource();
+    method public int getDrawableStyle();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyDrawableResource> CREATOR;
+  }
+
   public class DevicePolicyKeyguardService extends android.app.Service {
     ctor public DevicePolicyKeyguardService();
     method @Nullable public void dismiss();
@@ -1011,8 +1037,10 @@
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull int[]);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull java.util.Set<android.app.admin.DevicePolicyDrawableResource>);
     method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
     method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
@@ -1023,12 +1051,12 @@
     field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
     field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
     field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
-    field public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
-    field public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
-    field public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
+    field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
+    field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+    field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
     field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
-    field public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
+    field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
     field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
     field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -1044,6 +1072,7 @@
     field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
     field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
     field public static final int CODE_USER_SETUP_COMPLETED = 4; // 0x4
+    field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
@@ -1055,6 +1084,7 @@
     field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
     field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
     field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
+    field public static final String EXTRA_ROLE_HOLDER_STATE = "android.app.extra.ROLE_HOLDER_STATE";
     field public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4
     field public static final int FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1
     field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
@@ -1070,6 +1100,7 @@
     field public static final int RESULT_DEVICE_OWNER_SET = 123; // 0x7b
     field public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR = 1; // 0x1
     field public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2; // 0x2
+    field public static final int RESULT_UPDATE_ROLE_HOLDER = 2; // 0x2
     field public static final int RESULT_WORK_PROFILE_CREATED = 122; // 0x7a
     field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
     field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
@@ -3820,7 +3851,7 @@
   }
 
   public class HdmiDeviceInfo implements android.os.Parcelable {
-    ctor public HdmiDeviceInfo();
+    ctor @Deprecated public HdmiDeviceInfo();
     method public int describeContents();
     method public int getAdopterId();
     method public int getDeviceId();
@@ -3841,6 +3872,7 @@
     method public boolean isSourceType();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ADDR_INTERNAL = 0; // 0x0
+    field public static final int ADDR_INVALID = -1; // 0xffffffff
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.hdmi.HdmiDeviceInfo> CREATOR;
     field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
     field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
@@ -3854,6 +3886,7 @@
     field public static final int PATH_INTERNAL = 0; // 0x0
     field public static final int PATH_INVALID = 65535; // 0xffff
     field public static final int PORT_INVALID = -1; // 0xffffffff
+    field public static final int VENDOR_ID_UNKNOWN = 16777215; // 0xffffff
   }
 
   public final class HdmiHotplugEvent implements android.os.Parcelable {
@@ -6369,6 +6402,7 @@
 
   public static final class TvInputManager.Hardware {
     method public void overrideAudioSink(int, String, int, int, int);
+    method public void overrideAudioSink(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int, int, int);
     method public void setStreamVolume(float);
     method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig);
   }
@@ -8151,6 +8185,17 @@
 
   public static class NetworkStats.Entry {
     ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
+    method public int getDefaultNetwork();
+    method public int getMetered();
+    method public long getOperations();
+    method public int getRoaming();
+    method public long getRxBytes();
+    method public long getRxPackets();
+    method public int getSet();
+    method public int getTag();
+    method public long getTxBytes();
+    method public long getTxPackets();
+    method public int getUid();
   }
 
   @Deprecated public class RssiCurve implements android.os.Parcelable {
@@ -8187,6 +8232,7 @@
   public class TrafficStats {
     method public static void setThreadStatsTagApp();
     method public static void setThreadStatsTagBackup();
+    method public static void setThreadStatsTagDownload();
     method public static void setThreadStatsTagRestore();
     field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f
     field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80
@@ -9139,6 +9185,7 @@
     field public static final int EVENT_UNSPECIFIED = 0; // 0x0
     field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
     field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+    field public static final int REASON_BLUETOOTH_BROADCAST = 203; // 0xcb
     field public static final int REASON_GEOFENCING = 100; // 0x64
     field public static final int REASON_LOCATION_PROVIDER = 312; // 0x138
     field public static final int REASON_OTHER = 1; // 0x1
@@ -14022,14 +14069,21 @@
 
   public class ImsService extends android.app.Service {
     ctor public ImsService();
-    method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
-    method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
-    method public void disableIms(int);
-    method public void enableIms(int);
-    method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method @Nullable public android.telephony.ims.feature.MmTelFeature createEmergencyOnlyMmTelFeature(int);
+    method @Deprecated public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
+    method @Nullable public android.telephony.ims.feature.MmTelFeature createMmTelFeatureForSubscription(int, int);
+    method @Deprecated public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
+    method @Nullable public android.telephony.ims.feature.RcsFeature createRcsFeatureForSubscription(int, int);
+    method @Deprecated public void disableIms(int);
+    method public void disableImsForSubscription(int, int);
+    method @Deprecated public void enableIms(int);
+    method public void enableImsForSubscription(int, int);
+    method @Deprecated public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+    method @NonNull public android.telephony.ims.stub.ImsConfigImplBase getConfigForSubscription(int, int);
     method @NonNull public java.util.concurrent.Executor getExecutor();
     method public long getImsServiceCapabilities();
-    method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @Deprecated public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+    method @NonNull public android.telephony.ims.stub.ImsRegistrationImplBase getRegistrationForSubscription(int, int);
     method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
     method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
     method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 66250fce..f1b4624 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1147,15 +1147,15 @@
 
   public final class DisplayManager {
     method public boolean areUserDisabledHdrTypesAllowed();
-    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode();
+    method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
     method @NonNull public int[] getUserDisabledHdrTypes();
-    method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean isMinimalPostProcessingRequested(int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
     method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
     field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
     field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
@@ -1319,7 +1319,7 @@
     method public void setAccumulatedDeltaRangeMeters(double);
     method public void setAccumulatedDeltaRangeState(int);
     method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
-    method public void setAutomaticGainControlLevelInDb(double);
+    method @Deprecated public void setAutomaticGainControlLevelInDb(double);
     method public void setBasebandCn0DbHz(double);
     method @Deprecated public void setCarrierCycles(long);
     method public void setCarrierFrequencyHz(float);
@@ -1346,10 +1346,6 @@
     field public static final int ADR_STATE_ALL = 31; // 0x1f
   }
 
-  public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    ctor public GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]);
-  }
-
   public final class GnssNavigationMessage implements android.os.Parcelable {
     ctor public GnssNavigationMessage();
     method public void reset();
@@ -2714,11 +2710,14 @@
   }
 
   public final class Display {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode();
     method @NonNull public android.view.Display.Mode getDefaultMode();
     method @NonNull public int[] getReportedHdrTypes();
     method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
     method public int getType();
+    method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean hasAccess(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
     field public static final int FLAG_TRUSTED = 128; // 0x80
     field public static final int TYPE_EXTERNAL = 2; // 0x2
     field public static final int TYPE_INTERNAL = 1; // 0x1
@@ -2733,6 +2732,13 @@
     method public boolean matches(int, int, float);
   }
 
+  public static final class Display.Mode.Builder {
+    ctor public Display.Mode.Builder();
+    method @NonNull public android.view.Display.Mode build();
+    method @NonNull public android.view.Display.Mode.Builder setRefreshRate(float);
+    method @NonNull public android.view.Display.Mode.Builder setResolution(int, int);
+  }
+
   public class FocusFinder {
     method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean);
   }
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index e3690e5..01604e6 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -737,6 +737,8 @@
     
 MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
     
+MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int):
+    android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int)
 
 
 MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cf2b7ac..283345f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -90,6 +90,7 @@
 import android.transition.TransitionManager;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.Dumpable;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -145,6 +146,7 @@
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
 import com.android.internal.policy.PhoneWindow;
+import com.android.internal.util.dump.DumpableContainerImpl;
 
 import dalvik.system.VMRuntime;
 
@@ -954,6 +956,9 @@
 
     private SplashScreen mSplashScreen;
 
+    @Nullable
+    private DumpableContainerImpl mDumpableContainer;
+
     private final WindowControllerCallback mWindowControllerCallback =
             new WindowControllerCallback() {
         /**
@@ -7081,8 +7086,23 @@
         dumpInner(prefix, fd, writer, args);
     }
 
+    /**
+     * See {@link android.util.DumpableContainer#addDumpable(Dumpable)}.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public final boolean addDumpable(@NonNull Dumpable dumpable) {
+        if (mDumpableContainer == null) {
+            mDumpableContainer = new DumpableContainerImpl();
+        }
+        return mDumpableContainer.addDumpable(dumpable);
+    }
+
     void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
             @NonNull PrintWriter writer, @Nullable String[] args) {
+        String innerPrefix = prefix + "  ";
+
         if (args != null && args.length > 0) {
             // Handle special cases
             switch (args[0]) {
@@ -7095,12 +7115,33 @@
                 case "--translation":
                     dumpUiTranslation(prefix, writer);
                     return;
+                case "--list-dumpables":
+                    if (mDumpableContainer == null) {
+                        writer.print(prefix); writer.println("No dumpables");
+                        return;
+                    }
+                    mDumpableContainer.listDumpables(prefix, writer);
+                    return;
+                case "--dump-dumpable":
+                    if (args.length == 1) {
+                        writer.println("--dump-dumpable requires the dumpable name");
+                        return;
+                    }
+                    if (mDumpableContainer == null) {
+                        writer.println("no dumpables");
+                        return;
+                    }
+                    // Strips --dump-dumpable NAME
+                    String[] prunedArgs = new String[args.length - 2];
+                    System.arraycopy(args, 2, prunedArgs, 0, prunedArgs.length);
+                    mDumpableContainer.dumpOneDumpable(prefix, writer, args[1], prunedArgs);
+                    return;
             }
         }
+
         writer.print(prefix); writer.print("Local Activity ");
                 writer.print(Integer.toHexString(System.identityHashCode(this)));
                 writer.println(" State:");
-        String innerPrefix = prefix + "  ";
         writer.print(innerPrefix); writer.print("mResumed=");
                 writer.print(mResumed); writer.print(" mStopped=");
                 writer.print(mStopped); writer.print(" mFinished=");
@@ -7138,6 +7179,10 @@
         dumpUiTranslation(prefix, writer);
 
         ResourcesManager.getInstance().dump(prefix, writer);
+
+        if (mDumpableContainer != null) {
+            mDumpableContainer.dumpAllDumpables(prefix, writer, args);
+        }
     }
 
     void dumpContentCaptureManager(String prefix, PrintWriter writer) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 324e1ae..96487de 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -71,6 +71,9 @@
     }
 
     // Access modes for handleIncomingUser.
+    /**
+     * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
+     */
     public static final int ALLOW_NON_FULL = 0;
     /**
      * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
@@ -78,13 +81,18 @@
      * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
      */
     public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+    /**
+     * Allows access to a caller only if it has the full
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     */
     public static final int ALLOW_FULL_ONLY = 2;
     /**
      * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
-     * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
-     * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+     * if in the same profile group.
+     * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS} is required and suffices
+     * as in {@link #ALLOW_NON_FULL}.
      */
-    public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
+    public static final int ALLOW_PROFILES_OR_NON_FULL = 3;
 
     /**
      * Returns profile information in free form string in two separate strings.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fe512ff..fa48730 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -312,6 +312,14 @@
     @ContextType
     private int mContextType;
 
+    /**
+     * {@code true} to indicate that the {@link Context} owns the {@link #getWindowContextToken()}
+     * and is responsible for detaching the token when the Context is released.
+     *
+     * @see #finalize()
+     */
+    private boolean mOwnsToken = false;
+
     @GuardedBy("mSync")
     private File mDatabasesDir;
     @GuardedBy("mSync")
@@ -3015,7 +3023,7 @@
         // WindowContainer. We should detach from WindowContainer when the Context is finalized
         // if this Context is not a WindowContext. WindowContext finalization is handled in
         // WindowContext class.
-        if (mToken instanceof WindowTokenClient && mContextType != CONTEXT_TYPE_WINDOW_CONTEXT) {
+        if (mToken instanceof WindowTokenClient && mOwnsToken) {
             ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
         }
         super.finalize();
@@ -3046,6 +3054,7 @@
         token.attachContext(context);
         token.attachToDisplayContent(displayId);
         context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
+        context.mOwnsToken = true;
 
         return context;
     }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index fdcf99d..a82ecce 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -63,6 +63,7 @@
     boolean isInInvalidMsgState(String pkg, int uid);
     boolean hasUserDemotedInvalidMsgApp(String pkg, int uid);
     void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted);
+    boolean hasSentValidBubble(String pkg, int uid);
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     /**
      * Updates the notification's enabled state. Additionally locks importance for all of the
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index dc71a32..14afd0f 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,8 +41,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.persistentdata.IPersistentDataBlockService;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.IOnKeyguardExitResult;
 import android.view.IWindowManager;
@@ -49,6 +52,9 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
 import com.android.internal.widget.LockscreenCredential;
@@ -57,6 +63,8 @@
 import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * Class that can be used to lock and unlock the keyguard. The
@@ -69,10 +77,13 @@
     private static final String TAG = "KeyguardManager";
 
     private final Context mContext;
+    private final LockPatternUtils mLockPatternUtils;
     private final IWindowManager mWM;
     private final IActivityManager mAm;
     private final ITrustManager mTrustManager;
     private final INotificationManager mNotificationManager;
+    private final ArrayMap<WeakEscrowTokenRemovedListener, IWeakEscrowTokenRemovedListener>
+            mListeners = new ArrayMap<>();
 
     /**
      * Intent used to prompt user for device credentials.
@@ -455,8 +466,42 @@
         public void onDismissCancelled() { }
     }
 
+    /**
+     * Callback passed to
+     * {@link KeyguardManager#addWeakEscrowToken}
+     * to notify caller of state change.
+     * @hide
+     */
+    @SystemApi
+    public interface WeakEscrowTokenActivatedListener {
+        /**
+         * The method to be called when the token is activated.
+         * @param handle 64 bit handle corresponding to the escrow token
+         * @param user user for whom the weak escrow token has been added
+         */
+        void onWeakEscrowTokenActivated(long handle, @NonNull UserHandle user);
+    }
+
+    /**
+     * Listener passed to
+     * {@link KeyguardManager#registerWeakEscrowTokenRemovedListener} and
+     * {@link KeyguardManager#unregisterWeakEscrowTokenRemovedListener}
+     * to notify caller of an weak escrow token has been removed.
+     * @hide
+     */
+    @SystemApi
+    public interface WeakEscrowTokenRemovedListener {
+        /**
+         * The method to be called when the token is removed.
+         * @param handle 64 bit handle corresponding to the escrow token
+         * @param user user for whom the escrow token has been added
+         */
+        void onWeakEscrowTokenRemoved(long handle, @NonNull UserHandle user);
+    }
+
     KeyguardManager(Context context) throws ServiceNotFoundException {
         mContext = context;
+        mLockPatternUtils = new LockPatternUtils(context);
         mWM = WindowManagerGlobal.getWindowManagerService();
         mAm = ActivityManager.getService();
         mTrustManager = ITrustManager.Stub.asInterface(
@@ -785,7 +830,6 @@
             return false;
         }
 
-        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
         int userId = mContext.getUserId();
         if (isDeviceSecure(userId)) {
             Log.e(TAG, "Password already set, rejecting call to setLock");
@@ -799,7 +843,7 @@
         try {
             LockscreenCredential credential = createLockscreenCredential(
                     lockType, password);
-            success = lockPatternUtils.setLockCredential(
+            success = mLockPatternUtils.setLockCredential(
                     credential,
                     /* savedPassword= */ LockscreenCredential.createNone(),
                     userId);
@@ -813,6 +857,150 @@
     }
 
     /**
+     * Create a weak escrow token for the current user, which can later be used to unlock FBE
+     * or change user password.
+     *
+     * After adding, if the user currently  has a secure lockscreen, they will need to perform a
+     * confirm credential operation in order to activate the token for future use. If the user
+     * has no secure lockscreen, then the token is activated immediately.
+     *
+     * If the user changes or removes the lockscreen password, any activated weak escrow token will
+     * be removed.
+     *
+     * @return a unique 64-bit token handle which is needed to refer to this token later.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public long addWeakEscrowToken(@NonNull byte[] token, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WeakEscrowTokenActivatedListener listener) {
+        Objects.requireNonNull(token, "Token cannot be null.");
+        Objects.requireNonNull(user, "User cannot be null.");
+        Objects.requireNonNull(executor, "Executor cannot be null.");
+        Objects.requireNonNull(listener, "Listener cannot be null.");
+        int userId = user.getIdentifier();
+        IWeakEscrowTokenActivatedListener internalListener =
+                new IWeakEscrowTokenActivatedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenActivated(long handle, int userId) {
+                UserHandle user = UserHandle.of(userId);
+                final long restoreToken = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onWeakEscrowTokenActivated(handle, user));
+                } finally {
+                    Binder.restoreCallingIdentity(restoreToken);
+                }
+                Log.i(TAG, "Weak escrow token activated.");
+            }
+        };
+        return mLockPatternUtils.addWeakEscrowToken(token, userId, internalListener);
+    }
+
+    /**
+     * Remove a weak escrow token.
+     *
+     * @return true if the given handle refers to a valid weak token previously returned from
+     * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean removeWeakEscrowToken(long handle, @NonNull UserHandle user) {
+        Objects.requireNonNull(user, "User cannot be null.");
+        return mLockPatternUtils.removeWeakEscrowToken(handle, user.getIdentifier());
+    }
+
+    /**
+     * Check if the given weak escrow token is active or not.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean isWeakEscrowTokenActive(long handle, @NonNull UserHandle user) {
+        Objects.requireNonNull(user, "User cannot be null.");
+        return mLockPatternUtils.isWeakEscrowTokenActive(handle, user.getIdentifier());
+    }
+
+    /**
+     * Check if the given weak escrow token is validate.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean isWeakEscrowTokenValid(long handle, @NonNull byte[] token,
+            @NonNull UserHandle user) {
+        Objects.requireNonNull(token, "Token cannot be null.");
+        Objects.requireNonNull(user, "User cannot be null.");
+        return mLockPatternUtils.isWeakEscrowTokenValid(handle, token, user.getIdentifier());
+    }
+
+    /**
+     * Register the given WeakEscrowTokenRemovedListener.
+     *
+     * @return true if the listener is registered successfully, return false otherwise.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean registerWeakEscrowTokenRemovedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WeakEscrowTokenRemovedListener listener) {
+        Objects.requireNonNull(listener, "Listener cannot be null.");
+        Objects.requireNonNull(executor, "Executor cannot be null.");
+        Preconditions.checkArgument(!mListeners.containsKey(listener),
+                "Listener already registered: %s", listener);
+        IWeakEscrowTokenRemovedListener internalListener =
+                new IWeakEscrowTokenRemovedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenRemoved(long handle, int userId) {
+                UserHandle user = UserHandle.of(userId);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onWeakEscrowTokenRemoved(handle, user));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        };
+        if (mLockPatternUtils.registerWeakEscrowTokenRemovedListener(internalListener)) {
+            mListeners.put(listener, internalListener);
+            return true;
+        } else {
+            Log.e(TAG, "Listener failed to register");
+            return false;
+        }
+    }
+
+    /**
+     * Unregister the given WeakEscrowTokenRemovedListener.
+     *
+     * @return true if the listener is unregistered successfully, return false otherwise.
+     * @hide
+     */
+    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+    @RequiresPermission(Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN)
+    @SystemApi
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            @NonNull WeakEscrowTokenRemovedListener listener) {
+        Objects.requireNonNull(listener, "Listener cannot be null.");
+        IWeakEscrowTokenRemovedListener internalListener = mListeners.get(listener);
+        Preconditions.checkArgument(internalListener != null, "Listener was not registered");
+        if (mLockPatternUtils.unregisterWeakEscrowTokenRemovedListener(internalListener)) {
+            mListeners.remove(listener);
+            return true;
+        } else {
+            Log.e(TAG, "Listener failed to unregister.");
+            return false;
+        }
+    }
+
+    /**
      * Set the lockscreen password to {@code newPassword} after validating the current password
      * against {@code currentPassword}.
      * <p>If no password is currently set, {@code currentPassword} should be set to {@code null}.
@@ -832,13 +1020,12 @@
     })
     public boolean setLock(@LockTypes int newLockType, @Nullable byte[] newPassword,
             @LockTypes int currentLockType, @Nullable byte[] currentPassword) {
-        final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
         final int userId = mContext.getUserId();
         LockscreenCredential currentCredential = createLockscreenCredential(
                 currentLockType, currentPassword);
         LockscreenCredential newCredential = createLockscreenCredential(
                 newLockType, newPassword);
-        return lockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
+        return mLockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
     }
 
     /**
@@ -857,10 +1044,9 @@
             Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
     })
     public boolean checkLock(@LockTypes int lockType, @Nullable byte[] password) {
-        final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
         final LockscreenCredential credential = createLockscreenCredential(
                 lockType, password);
-        final VerifyCredentialResponse response = lockPatternUtils.verifyCredential(
+        final VerifyCredentialResponse response = mLockPatternUtils.verifyCredential(
                 credential, mContext.getUserId(), /* flags= */ 0);
         if (response == null) {
             return false;
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
new file mode 100644
index 0000000..436eac3
--- /dev/null
+++ b/core/java/android/app/LocaleConfig.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.LocaleList;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The LocaleConfig of an application.
+ * Defined in an XML resource file with an {@code <locale-config>} element and
+ * referenced in the manifest via {@code android:localeConfig} on
+ * {@code <application>}.
+ *
+ * For more information, see TODO(b/214154050): add link to guide
+ *
+ * @attr ref android.R.styleable#LocaleConfig_Locale_name
+ * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig
+ */
+public class LocaleConfig {
+
+    private static final String TAG = "LocaleConfig";
+    public static final String TAG_LOCALE_CONFIG = "locale-config";
+    public static final String TAG_LOCALE = "locale";
+    private LocaleList mLocales;
+    private int mStatus;
+
+    /**
+     * succeeded reading the LocaleConfig structure stored in an XML file.
+     */
+    public static final int STATUS_SUCCESS = 0;
+    /**
+     * No android:localeConfig tag on <application>.
+     */
+    public static final int STATUS_NOT_SPECIFIED = 1;
+    /**
+     * Malformed input in the XML file where the LocaleConfig was stored.
+     */
+    public static final int STATUS_PARSING_FAILED = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_SUCCESS,
+            STATUS_NOT_SPECIFIED,
+            STATUS_PARSING_FAILED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Status{}
+
+    /**
+     * Returns the LocaleConfig for the provided application context
+     *
+     * @param context the context of the application
+     *
+     * @see Context#createPackageContext(String, int).
+     */
+    public LocaleConfig(@NonNull Context context) {
+        int resId = 0;
+        Resources res = context.getResources();
+        try {
+            //Get the resource id
+            resId = new ApplicationInfo(context.getApplicationInfo()).getLocaleConfigRes();
+            //Get the parser to read XML data
+            XmlResourceParser parser = res.getXml(resId);
+            parseLocaleConfig(parser, res);
+        } catch (Resources.NotFoundException e) {
+            Slog.w(TAG, "The resource file pointed to by the given resource ID isn't found.", e);
+            mStatus = STATUS_NOT_SPECIFIED;
+        } catch (XmlPullParserException | IOException e) {
+            Slog.w(TAG, "Failed to parse XML configuration from "
+                    + res.getResourceEntryName(resId), e);
+            mStatus = STATUS_PARSING_FAILED;
+        }
+    }
+
+    /**
+     * Parse the XML content and get the locales supported by the application
+     */
+    private void parseLocaleConfig(XmlResourceParser parser, Resources res)
+            throws IOException, XmlPullParserException {
+        XmlUtils.beginDocument(parser, TAG_LOCALE_CONFIG);
+        int outerDepth = parser.getDepth();
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        Set<String> localeNames = new HashSet<String>();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (TAG_LOCALE.equals(parser.getName())) {
+                final TypedArray attributes = res.obtainAttributes(
+                        attrs, com.android.internal.R.styleable.LocaleConfig_Locale);
+                String nameAttr = attributes.getString(
+                        com.android.internal.R.styleable.LocaleConfig_Locale_name);
+                localeNames.add(nameAttr);
+                attributes.recycle();
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        mStatus = STATUS_SUCCESS;
+        mLocales = LocaleList.forLanguageTags(String.join(",", localeNames));
+    }
+
+    /**
+     * Returns the locales supported by the specified application.
+     *
+     * <p><b>Note:</b> The locale format should follow the
+     * <a href="https://www.rfc-editor.org/rfc/bcp/bcp47.txt">IETF BCP47 regular expression</a>
+     *
+     * @return the {@link LocaleList}
+     */
+    public @Nullable LocaleList getSupportedLocales() {
+        return mLocales;
+    }
+
+    /**
+     * Get the status of reading the resource file where the LocaleConfig was stored.
+     *
+     * <p>Distinguish "the application didn't provide the resource file" from "the application
+     * provided malformed input" if {@link #getSupportedLocales()} returns {@code null}.
+     *
+     * @return {@code STATUS_SUCCESS} if the LocaleConfig structure existed in an XML file was
+     * successfully read, or {@code STATUS_NOT_SPECIFIED} if no android:localeConfig tag on
+     * <application> pointing to an XML file that stores the LocaleConfig, or
+     * {@code STATUS_PARSING_FAILED} if the application provided malformed input for the
+     * LocaleConfig structure.
+     *
+     * @see #STATUS_SUCCESS
+     * @see #STATUS_NOT_SPECIFIED
+     * @see #STATUS_PARSING_FAILED
+     *
+     */
+    public @Status int getStatus() {
+        return mStatus;
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/core/java/android/app/admin/DevicePolicyDrawableResource.aidl
similarity index 81%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to core/java/android/app/admin/DevicePolicyDrawableResource.aidl
index 6041460..6b73d981 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/core/java/android/app/admin/DevicePolicyDrawableResource.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package android.app.admin;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable DevicePolicyDrawableResource;
diff --git a/core/java/android/app/admin/DevicePolicyDrawableResource.java b/core/java/android/app/admin/DevicePolicyDrawableResource.java
new file mode 100644
index 0000000..d32ff84
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyDrawableResource.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 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.app.admin;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Used to pass in the required information for updating an enterprise drawable resource using
+ * {@link DevicePolicyManager#setDrawables}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DevicePolicyDrawableResource implements Parcelable {
+    private final @DevicePolicyResources.UpdatableDrawableId int mDrawableId;
+    private final @DevicePolicyResources.UpdatableDrawableStyle int mDrawableStyle;
+    private final @DevicePolicyResources.UpdatableDrawableSource int mDrawableSource;
+    private final @DrawableRes int mCallingPackageResourceId;
+    @NonNull private ParcelableResource mResource;
+
+    /**
+     * Creates an object containing the required information for updating an enterprise drawable
+     * resource using {@link DevicePolicyManager#setDrawables}.
+     *
+     * <p>It will be used to update the drawable defined by {@code drawableId} with style
+     * {@code drawableStyle} located in source {@code drawableSource} to the drawable with ID
+     * {@code callingPackageResourceId} in the calling package</p>
+     *
+     * @param drawableId The ID of the drawable to update.
+     * @param drawableStyle The style of the drawable to update.
+     * @param drawableSource The source of the drawable to update.
+     * @param callingPackageResourceId The ID of the drawable resource in the calling package to
+     *        use as an updated resource.
+     *
+     * @throws IllegalStateException if the resource with ID
+     * {@code callingPackageResourceId} doesn't exist in the {@code context} package.
+     */
+    public DevicePolicyDrawableResource(
+            @NonNull Context context,
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @DrawableRes int callingPackageResourceId) {
+        this(drawableId, drawableStyle, drawableSource, callingPackageResourceId,
+                new ParcelableResource(context, callingPackageResourceId,
+                        ParcelableResource.RESOURCE_TYPE_DRAWABLE));
+    }
+
+    private DevicePolicyDrawableResource(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @DrawableRes int callingPackageResourceId,
+            @NonNull ParcelableResource resource) {
+        this.mDrawableId = drawableId;
+        this.mDrawableStyle = drawableStyle;
+        this.mDrawableSource = drawableSource;
+        this.mCallingPackageResourceId = callingPackageResourceId;
+        this.mResource = resource;
+    }
+
+    /**
+     * Creates an object containing the required information for updating an enterprise drawable
+     * resource using {@link DevicePolicyManager#setDrawables}.
+     * <p>It will be used to update the drawable defined by {@code drawableId} with style
+     * {@code drawableStyle} to the drawable with ID {@code callingPackageResourceId} in the
+     * calling package</p>
+     *
+     * @param drawableId The ID of the drawable to update.
+     * @param drawableStyle The style of the drawable to update.
+     * @param callingPackageResourceId The ID of the drawable resource in the calling package to
+     *        use as an updated resource.
+     *
+     * @throws IllegalStateException if the resource with ID
+     * {@code callingPackageResourceId} doesn't exist in the calling package.
+     */
+    public DevicePolicyDrawableResource(
+            @NonNull Context context,
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DrawableRes int callingPackageResourceId) {
+       this(context, drawableId, drawableStyle, DevicePolicyResources.Drawable.Source.UNDEFINED,
+               callingPackageResourceId);
+    }
+
+    /**
+     * Returns the ID of the drawable to update.
+     */
+    @DevicePolicyResources.UpdatableDrawableId
+    public int getDrawableId() {
+        return mDrawableId;
+    }
+
+    /**
+     * Returns the style of the drawable to update
+     */
+    @DevicePolicyResources.UpdatableDrawableStyle
+    public int getDrawableStyle() {
+        return mDrawableStyle;
+    }
+
+    /**
+     * Returns the source of the drawable to update.
+     */
+    @DevicePolicyResources.UpdatableDrawableSource
+    public int getDrawableSource() {
+        return mDrawableSource;
+    }
+
+    /**
+     * Returns the ID of the drawable resource in the calling package to use as an updated
+     * resource.
+     */
+    @DrawableRes
+    public int getCallingPackageResourceId() {
+        return mCallingPackageResourceId;
+    }
+
+    /**
+     * Returns the {@link ParcelableResource} of the drawable.
+     *
+     * @hide
+     */
+    @NonNull
+    public ParcelableResource getResource() {
+        return mResource;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DevicePolicyDrawableResource other = (DevicePolicyDrawableResource) o;
+        return mDrawableId == other.mDrawableId
+                && mDrawableStyle == other.mDrawableStyle
+                && mDrawableSource == other.mDrawableSource
+                && mCallingPackageResourceId == other.mCallingPackageResourceId
+                && mResource.equals(other.mResource);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mDrawableId, mDrawableStyle, mDrawableSource, mCallingPackageResourceId, mResource);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDrawableId);
+        dest.writeInt(mDrawableStyle);
+        dest.writeInt(mDrawableSource);
+        dest.writeInt(mCallingPackageResourceId);
+        dest.writeTypedObject(mResource, flags);
+    }
+
+    public static final @NonNull Creator<DevicePolicyDrawableResource> CREATOR =
+            new Creator<DevicePolicyDrawableResource>() {
+                @Override
+                public DevicePolicyDrawableResource createFromParcel(Parcel in) {
+                    int drawableId = in.readInt();
+                    int drawableStyle = in.readInt();
+                    int drawableSource = in.readInt();
+                    int callingPackageResourceId = in.readInt();
+                    ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR);
+
+                    return new DevicePolicyDrawableResource(
+                            drawableId, drawableStyle, drawableSource, callingPackageResourceId,
+                            resource);
+                }
+
+                @Override
+                public DevicePolicyDrawableResource[] newArray(int size) {
+                    return new DevicePolicyDrawableResource[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 73ebdf6..2bfb938 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,9 @@
 
 package android.app.admin;
 
+import static android.app.admin.DevicePolicyResources.Drawable.INVALID_ID;
+import static android.app.admin.DevicePolicyResources.Drawable.Source.UNDEFINED;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.Manifest.permission;
@@ -52,7 +55,9 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
 import android.net.PrivateDnsConnectivityChecker;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -89,6 +94,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.DebugUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
 
@@ -123,6 +129,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -480,6 +487,10 @@
      *
      * <p>Device management role holders are required to have a handler for this intent action.
      *
+     * <p>If {@link #EXTRA_ROLE_HOLDER_STATE} is supplied to this intent, it is the responsibility
+     * of the role holder to restore its state from this extra. This is the same {@link Bundle}
+     * which the role holder returns alongside {@link #RESULT_UPDATE_ROLE_HOLDER}.
+     *
      * <p>A result code of {@link Activity#RESULT_OK} implies that managed profile provisioning
      * finished successfully. If it did not, a result code of {@link Activity#RESULT_CANCELED}
      * is used instead.
@@ -488,8 +499,7 @@
      *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE =
@@ -528,6 +538,10 @@
      *
      * <p>Device management role holders are required to have a handler for this intent action.
      *
+     * <p>If {@link #EXTRA_ROLE_HOLDER_STATE} is supplied to this intent, it is the responsibility
+     * of the role holder to restore its state from this extra. This is the same {@link Bundle}
+     * which the role holder returns alongside {@link #RESULT_UPDATE_ROLE_HOLDER}.
+     *
      * <p>The result codes can be either {@link #RESULT_WORK_PROFILE_CREATED}, {@link
      * #RESULT_DEVICE_OWNER_SET} or {@link Activity#RESULT_CANCELED} if provisioning failed.
      *
@@ -535,8 +549,7 @@
      *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE =
@@ -561,14 +574,54 @@
      *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION =
             "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
 
     /**
+     * {@link Activity} result code which can be returned by {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} and {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} to signal that an update
+     * to the role holder is required.
+     *
+     * <p>This result code must be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_UPDATE_ROLE_HOLDER = 2;
+
+    /**
+     * A {@link Bundle} extra which describes the state of the role holder at the time when it
+     * returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
+     *
+     * <p>After the update completes, the role holder's {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent will be relaunched,
+     * which will contain this extra. It is the role holder's responsibility to restore its
+     * state from this extra.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ROLE_HOLDER_STATE = "android.app.extra.ROLE_HOLDER_STATE";
+
+    /**
+     * A {@code boolean} extra which determines whether to force a role holder update, regardless
+     * of any internal conditions {@link #ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER} might have.
+     *
+     * <p>This extra can be provided to intents with action {@link
+     * #ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER =
+            "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
+
+    /**
      * Action: Bugreport sharing with device owner has been accepted by the user.
      *
      * @hide
@@ -2857,10 +2910,12 @@
      * #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters a problem
      * that will not be solved by relaunching it again.
      *
+     * <p>If this activity has additional internal conditions which are not met, it should return
+     * {@link #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR}.
+     *
      * @hide
      */
-    // TODO(b/208628038): Uncomment when the permission is in place
-    //  @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+    @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
     public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER =
@@ -3185,6 +3240,36 @@
      */
     public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1;
 
+    /**
+     * Broadcast action: notify system apps (e.g. settings, SysUI, etc) that the device management
+     * resources with IDs {@link #EXTRA_RESOURCE_ID} has been updated using, the updated resources
+     * can be retrieved using {@link #getDrawable}.
+     *
+     * <p>This broadcast is sent to registered receivers only.
+     *
+     * <p> The following extras will be included to identify the type of resource being updated:
+     * <ul>
+     *     <li>{@link #EXTRA_RESOURCE_TYPE_DRAWABLE} for drawable resources</li>
+     * </ul>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED =
+            "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED";
+
+    /**
+     * A boolean extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate that a
+     * resource of type {@link Drawable} is being updated.
+     */
+    public static final String EXTRA_RESOURCE_TYPE_DRAWABLE =
+            "android.app.extra.RESOURCE_TYPE_DRAWABLE";
+
+    /**
+     * An integer array extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate which
+     * drawable IDs (see {@link DevicePolicyResources.UpdatableDrawableId}) have been updated.
+     */
+    public static final String EXTRA_RESOURCE_ID =
+            "android.app.extra.RESOURCE_ID";
+
     /** @hide */
     @NonNull
     @TestApi
@@ -14376,4 +14461,243 @@
     public Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
         return ProvisioningIntentHelper.createProvisioningIntentFromNfcIntent(nfcIntent);
     }
+
+    /**
+     * For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if
+     * {@link DevicePolicyDrawableResource#getDrawableSource()} is not set or is set to
+     * {@link DevicePolicyResources.Drawable.Source#UNDEFINED}, it updates the drawable resource for
+     * the combination of {@link DevicePolicyDrawableResource#getDrawableId()} and
+     * {@link DevicePolicyDrawableResource#getDrawableStyle()}, (see
+     * {@link DevicePolicyResources.Drawable} and {@link DevicePolicyResources.Drawable.Style}) to
+     * the drawable with ID {@link DevicePolicyDrawableResource#getCallingPackageResourceId()},
+     * meaning any system UI surface calling {@link #getDrawable}
+     * with {@code drawableId} and {@code drawableStyle} will get the new resource after this API
+     * is called.
+     *
+     * <p>Otherwise, if {@link DevicePolicyDrawableResource#getDrawableSource()} is set (see
+     * {@link DevicePolicyResources.Drawable.Source}, it overrides any drawables that was set for
+     * the same {@code drawableId} and {@code drawableStyle} for the provided source.
+     *
+     * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+     * registered receivers when a resource has been updated successfully.
+     *
+     * <p>Important notes to consider when using this API:
+     * <ul>
+     * <li>{@link #getDrawable} references the resource
+     * {@link DevicePolicyDrawableResource#getCallingPackageResourceId()} in the
+     * calling package each time it gets called. You have to ensure that the resource is always
+     * available in the calling package as long as it is used as an updated resource.
+     * <li>You still have to re-call {@code setDrawables} even if you only make changes to the
+     * content of the resource with ID
+     * {@link DevicePolicyDrawableResource#getCallingPackageResourceId()} as the content might be
+     * cached and would need updating.
+     * </ul>
+     *
+     * @param drawables The list of {@link DevicePolicyDrawableResource} to update.
+     *
+     * @throws IllegalArgumentException if {@link DevicePolicyDrawableResource#getDrawableId()},
+     * {@link DevicePolicyDrawableResource#getDrawableStyle()}, or
+     * {@link DevicePolicyDrawableResource#getDrawableSource()} aren't defined in
+     * {@link DevicePolicyResources.Drawable}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+    public void setDrawables(@NonNull Set<DevicePolicyDrawableResource> drawables) {
+        if (mService != null) {
+            try {
+                mService.setDrawables(new ArrayList<>(drawables));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes all updated drawables for the list of {@code drawableIds} (see
+     * {@link DevicePolicyResources.Drawable} that was previously set by calling
+     * {@link #setDrawables}, meaning any subsequent calls to {@link #getDrawable} for the provided
+     * IDs with any {@link DevicePolicyResources.Drawable.Style} and any
+     * {@link DevicePolicyResources.Drawable.Source} will return the default drawable from
+     * {@code defaultDrawableLoader}.
+     *
+     * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+     * registered receivers when a resource has been reset successfully.
+     *
+     * @param drawableIds The list of IDs  to remove.
+     *
+     * @throws IllegalArgumentException if IDs are not defined in
+     * {@link DevicePolicyResources.Drawable}
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+    public void resetDrawables(@NonNull int[] drawableIds) {
+        if (mService != null) {
+            try {
+                mService.resetDrawables(drawableIds);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the appropriate updated drawable for the {@code drawableId}
+     * (see {@link DevicePolicyResources.Drawable}), with style {@code drawableStyle}
+     * (see {@link DevicePolicyResources.Drawable.Style}) if one was set using
+     * {@code setDrawables}, otherwise returns the drawable from {@code defaultDrawableLoader}.
+     *
+     * <p>Also returns the drawable from {@code defaultDrawableLoader} if
+     * {@link DevicePolicyResources.Drawable#INVALID_ID} was passed.
+     *
+     * <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to
+     * set a different value use
+     * {@link #getDrawableForDensity(int, int, int, Callable)}.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * <p>Note that each call to this API loads the resource from the package that called
+     * {@code setDrawables} to set the updated resource.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @Nullable
+    public Drawable getDrawable(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        return getDrawable(drawableId, drawableStyle, UNDEFINED, defaultDrawableLoader);
+    }
+
+    /**
+     * Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
+     * a {@code drawableSource} (see {@link DevicePolicyResources.Drawable.Source}) which
+     * could result in returning a different drawable than {@link #getDrawable(int, int, Callable)}
+     * if an override was set for that specific source.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param drawableSource The source for the caller.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @Nullable
+    public Drawable getDrawable(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
+        if (drawableId == INVALID_ID) {
+            return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+        }
+        if (mService != null) {
+            try {
+                ParcelableResource resource = mService.getDrawable(
+                        drawableId, drawableStyle, drawableSource);
+                if (resource == null) {
+                    return ParcelableResource.loadDefaultDrawable(
+                            defaultDrawableLoader);
+                }
+                return resource.getDrawable(
+                        mContext,
+                        /* density= */ 0,
+                        defaultDrawableLoader);
+
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Error getting the updated drawable from DevicePolicyManagerService.",
+                        e);
+                return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+            }
+        }
+        return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+    }
+
+    /**
+     * Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
+     * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param density The desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}. A value of 0 means to use the
+     *            density returned from {@link Resources#getConfiguration()}.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @Nullable
+    public Drawable getDrawableForDensity(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            int density,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        return getDrawableForDensity(
+                drawableId,
+                drawableStyle,
+                UNDEFINED,
+                density,
+                defaultDrawableLoader);
+    }
+
+     /**
+     * Similar to {@link #getDrawable(int, int, int, Callable)}, but also accepts
+     * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
+     *
+     * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+     * notified when a resource has been updated.
+     *
+     * @param drawableId The drawable ID to get the updated resource for.
+     * @param drawableStyle The drawable style to use.
+     * @param drawableSource The source for the caller.
+     * @param density The desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}. A value of 0 means to use the
+     *            density returned from {@link Resources#getConfiguration()}.
+     * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
+     *                              the provided params.
+     */
+    @Nullable
+    public Drawable getDrawableForDensity(
+            @DevicePolicyResources.UpdatableDrawableId int drawableId,
+            @DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
+            @DevicePolicyResources.UpdatableDrawableSource int drawableSource,
+            int density,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
+        if (drawableId == INVALID_ID) {
+            return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+        }
+        if (mService != null) {
+            try {
+                ParcelableResource resource = mService.getDrawable(
+                        drawableId, drawableStyle, drawableSource);
+                if (resource == null) {
+                    return ParcelableResource.loadDefaultDrawable(
+                            defaultDrawableLoader);
+                }
+                return resource.getDrawable(mContext, density, defaultDrawableLoader);
+            } catch (RemoteException e) {
+                Log.e(
+                        TAG,
+                        "Error getting the updated drawable from DevicePolicyManagerService.",
+                        e);
+                return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+            }
+        }
+        return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
new file mode 100644
index 0000000..5133f26
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 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.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class containing the required identifiers to update device management resources.
+ *
+ * <p>See {@link DevicePolicyManager#getDrawable}.
+ *
+ */
+public final class DevicePolicyResources {
+
+    /**
+     * Resource identifiers used to update device management-related system drawable resources.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Drawable.INVALID_ID,
+            Drawable.WORK_PROFILE_ICON_BADGE,
+            Drawable.WORK_PROFILE_ICON,
+            Drawable.WORK_PROFILE_OFF_ICON,
+            Drawable.WORK_PROFILE_USER_ICON
+    })
+    public @interface UpdatableDrawableId {}
+
+    /**
+     * Identifiers to specify the desired style for the updatable device management system
+     * resource.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Drawable.Style.SOLID_COLORED,
+            Drawable.Style.SOLID_NOT_COLORED,
+            Drawable.Style.OUTLINE,
+    })
+    public @interface UpdatableDrawableStyle {}
+
+    /**
+     * Identifiers to specify the location if the updatable device management system resource.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Drawable.Source.UNDEFINED,
+            Drawable.Source.NOTIFICATION,
+            Drawable.Source.PROFILE_SWITCH_ANIMATION,
+            Drawable.Source.HOME_WIDGET,
+            Drawable.Source.LAUNCHER_OFF_BUTTON,
+            Drawable.Source.QUICK_SETTINGS,
+            Drawable.Source.STATUS_BAR
+    })
+    public @interface UpdatableDrawableSource {}
+
+
+    /**
+     * Class containing the identifiers used to update device management-related system drawable.
+     */
+    public static final class Drawable {
+
+        private Drawable() {
+        }
+
+        /**
+         * An ID for any drawable that can't be updated.
+         */
+        public static final int INVALID_ID = -1;
+
+        /**
+         * Specifically used to badge work profile app icons.
+         */
+        public static final int WORK_PROFILE_ICON_BADGE = 0;
+
+        /**
+         * General purpose work profile icon (i.e. generic icon badging). For badging app icons
+         * specifically, see {@link #WORK_PROFILE_ICON_BADGE}.
+         */
+        public static final int WORK_PROFILE_ICON = 1;
+
+        /**
+         * General purpose icon representing the work profile off state.
+         */
+        public static final int WORK_PROFILE_OFF_ICON = 2;
+
+        /**
+         * General purpose icon for the work profile user avatar.
+         */
+        public static final int WORK_PROFILE_USER_ICON = 3;
+
+        /**
+         * @hide
+         */
+        public static final Set<Integer> UPDATABLE_DRAWABLE_IDS = buildDrawablesSet();
+
+        private static Set<Integer> buildDrawablesSet() {
+            Set<Integer> drawables = new HashSet<>();
+            drawables.add(WORK_PROFILE_ICON_BADGE);
+            drawables.add(WORK_PROFILE_ICON);
+            drawables.add(WORK_PROFILE_OFF_ICON);
+            drawables.add(WORK_PROFILE_USER_ICON);
+            return drawables;
+        }
+
+        /**
+         * Class containing the source identifiers used to update device management-related system
+         * drawable.
+         */
+        public static final class Source {
+
+            private Source() {
+            }
+
+            /**
+             * A source identifier indicating that the updatable resource is used in a generic
+             * undefined location.
+             */
+            public static final int UNDEFINED = -1;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in notifications.
+             */
+            public static final int NOTIFICATION = 0;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in a cross
+             * profile switching animation.
+             */
+            public static final int PROFILE_SWITCH_ANIMATION = 1;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in a work
+             * profile home screen widget.
+             */
+            public static final int HOME_WIDGET = 2;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in the launcher
+             * turn off work button.
+             */
+            public static final int LAUNCHER_OFF_BUTTON = 3;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in quick settings.
+             */
+            public static final int QUICK_SETTINGS = 4;
+
+            /**
+             * A source identifier indicating that the updatable drawable is used in the status bar.
+             */
+            public static final int STATUS_BAR = 5;
+
+            /**
+             * @hide
+             */
+            public static final Set<Integer> UPDATABLE_DRAWABLE_SOURCES = buildSourcesSet();
+
+            private static Set<Integer> buildSourcesSet() {
+                Set<Integer> sources = new HashSet<>();
+                sources.add(UNDEFINED);
+                sources.add(NOTIFICATION);
+                sources.add(PROFILE_SWITCH_ANIMATION);
+                sources.add(HOME_WIDGET);
+                sources.add(LAUNCHER_OFF_BUTTON);
+                sources.add(QUICK_SETTINGS);
+                sources.add(STATUS_BAR);
+                return sources;
+            }
+        }
+
+        /**
+         * Class containing the style identifiers used to update device management-related system
+         * drawable.
+         */
+        @SuppressLint("StaticUtils")
+        public static final class Style {
+
+            private Style() {
+            }
+
+            /**
+             * A style identifier indicating that the updatable drawable should use the default
+             * style.
+             */
+            public static final int DEFAULT = -1;
+
+            /**
+             * A style identifier indicating that the updatable drawable has a solid color fill.
+             */
+            public static final int SOLID_COLORED = 0;
+
+            /**
+             * A style identifier indicating that the updatable drawable has a solid non-colored
+             * fill.
+             */
+            public static final int SOLID_NOT_COLORED = 1;
+
+            /**
+             * A style identifier indicating that the updatable drawable is an outline.
+             */
+            public static final int OUTLINE = 2;
+
+            /**
+             * @hide
+             */
+            public static final Set<Integer> UPDATABLE_DRAWABLE_STYLES = buildStylesSet();
+
+            private static Set<Integer> buildStylesSet() {
+                Set<Integer> styles = new HashSet<>();
+                styles.add(DEFAULT);
+                styles.add(SOLID_COLORED);
+                styles.add(SOLID_NOT_COLORED);
+                styles.add(OUTLINE);
+                return styles;
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b9fcdf5..8320087 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -17,6 +17,8 @@
 
 package android.app.admin;
 
+import android.app.admin.DevicePolicyDrawableResource;
+import android.app.admin.ParcelableResource;
 import android.app.admin.NetworkEvent;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
@@ -531,4 +533,7 @@
     boolean canUsbDataSignalingBeDisabled();
 
     List<UserHandle> listForegroundAffiliatedUsers();
+    void setDrawables(in List<DevicePolicyDrawableResource> resource);
+    void resetDrawables(in int[] drawableIds);
+    ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource);
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/core/java/android/app/admin/ParcelableResource.aidl
similarity index 82%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to core/java/android/app/admin/ParcelableResource.aidl
index 6041460..dd2b975 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/core/java/android/app/admin/ParcelableResource.aidl
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
+
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package android.app.admin;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable ParcelableResource;
diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java
new file mode 100644
index 0000000..e517162
--- /dev/null
+++ b/core/java/android/app/admin/ParcelableResource.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2022 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.app.admin;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.AnyRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+
+/**
+ * Used to store the required information to load a resource that was updated using
+ * {@link DevicePolicyManager#setDrawables}.
+ *
+ * @hide
+ */
+public final class ParcelableResource implements Parcelable {
+
+    private static String TAG = "DevicePolicyManager";
+
+    private static final String ATTR_RESOURCE_ID = "resource-id";
+    private static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_RESOURCE_NAME = "resource-name";
+    private static final String ATTR_RESOURCE_TYPE = "resource-type";
+
+    public static final int RESOURCE_TYPE_DRAWABLE = 1;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "RESOURCE_TYPE_" }, value = {
+            RESOURCE_TYPE_DRAWABLE
+    })
+    public @interface ResourceType {}
+
+    private final int mResourceId;
+    @NonNull private final String mPackageName;
+    @NonNull private final String mResourceName;
+    private final int mResourceType;
+
+    /**
+     *
+     * Creates a {@code ParcelableDevicePolicyResource} for the given {@code resourceId} and
+     * verifies that it exists in the package of the given {@code context}.
+     *
+     * @param context for the package containing the {@code resourceId} to use as the updated
+     *                resource
+     * @param resourceId of the resource to use as an updated resource
+     * @param resourceType see {@link ResourceType}
+     * @throws IllegalArgumentException if the given {@code resourceId} doesn't exist in the
+     * {@link Context#getResources()} of the given {@code context}
+     */
+    public ParcelableResource(@NonNull Context context, @AnyRes int resourceId,
+            @ResourceType int resourceType) throws IllegalArgumentException {
+        Objects.requireNonNull(context, "context must be provided");
+
+        verifyResourceExistsInCallingPackage(context, resourceId, resourceType);
+
+        this.mResourceId = resourceId;
+        this.mPackageName = context.getResources().getResourcePackageName(resourceId);
+        this.mResourceName = context.getResources().getResourceName(resourceId);
+        this.mResourceType = resourceType;
+    }
+
+    /**
+     * Creates a {@code ParcelableDevicePolicyResource} with the given params, this DOES NOT make
+     * any verifications on whether the given {@code resourceId} actually exists.
+     */
+    private ParcelableResource(
+            @AnyRes int resourceId, @NonNull String packageName, @NonNull String resourceName,
+            @ResourceType int resourceType) {
+        this.mResourceId = resourceId;
+        this.mPackageName = requireNonNull(packageName);
+        this.mResourceName = requireNonNull(resourceName);
+        this.mResourceType = resourceType;
+    }
+
+    private static void verifyResourceExistsInCallingPackage(
+            Context context, @AnyRes int resourceId, @ResourceType int resourceType)
+            throws IllegalArgumentException {
+        switch (resourceType) {
+            case RESOURCE_TYPE_DRAWABLE:
+                if (!hasDrawableInCallingPackage(context, resourceId)) {
+                    throw new IllegalArgumentException(String.format(
+                            "Drawable with id %d doesn't exist in the calling package %s",
+                            resourceId,
+                            context.getPackageName()));
+                }
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown ParcelableDevicePolicyResourceType: " + resourceType);
+        }
+    }
+
+    private static boolean hasDrawableInCallingPackage(Context context, @AnyRes int resourceId) {
+        try {
+            return context.getDrawable(resourceId) != null;
+        } catch (Resources.NotFoundException e) {
+            return false;
+        }
+    }
+
+    public @AnyRes int getResourceId() {
+        return mResourceId;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @NonNull
+    public String getResourceName() {
+        return mResourceName;
+    }
+
+    public int getResourceType() {
+        return mResourceType;
+    }
+
+    /**
+     * Loads the drawable with id {@code mResourceId} from {@code mPackageName} using the provided
+     * {@code density} and {@link Resources.Theme} and {@link Resources#getConfiguration} of the
+     * provided {@code context}.
+     *
+     * <p>Returns the default drawable by calling the {@code defaultDrawableLoader} if the updated
+     * drawable was not found or could not be loaded.</p>
+     */
+    @Nullable
+    public Drawable getDrawable(
+            Context context,
+            int density,
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        // TODO(b/203548565): properly handle edge case when the device manager role holder is
+        //  unavailable because it's being updated.
+        try {
+            Resources resources = getAppResourcesWithCallersConfiguration(context);
+            verifyResourceName(resources);
+            return resources.getDrawableForDensity(mResourceId, density, context.getTheme());
+        } catch (PackageManager.NameNotFoundException | RuntimeException e) {
+            Slog.e(TAG, "Unable to load drawable resource " + mResourceName, e);
+            return loadDefaultDrawable(defaultDrawableLoader);
+        }
+    }
+
+    private Resources getAppResourcesWithCallersConfiguration(Context context)
+            throws PackageManager.NameNotFoundException {
+        PackageManager pm = context.getPackageManager();
+        ApplicationInfo ai = pm.getApplicationInfo(
+                mPackageName,
+                PackageManager.MATCH_UNINSTALLED_PACKAGES
+                        | PackageManager.GET_SHARED_LIBRARY_FILES);
+        return pm.getResourcesForApplication(ai, context.getResources().getConfiguration());
+    }
+
+    private void verifyResourceName(Resources resources) throws IllegalStateException {
+        String name = resources.getResourceName(mResourceId);
+        if (!mResourceName.equals(name)) {
+            throw new IllegalStateException(String.format("Current resource name %s for resource id"
+                            + " %d has changed from the previously stored resource name %s.",
+                    name, mResourceId, mResourceName));
+        }
+    }
+
+    /**
+     * returns the {@link Drawable} loaded from calling
+     * {@code defaultDrawableLoader}.
+     */
+    public static Drawable loadDefaultDrawable(
+            @NonNull Callable<Drawable> defaultDrawableLoader) {
+        try {
+            return defaultDrawableLoader.call();
+        } catch (Exception e) {
+            throw new RuntimeException("Couldn't load default drawable", e);
+        }
+    }
+
+    /**
+     * Writes the content of the current {@code ParcelableDevicePolicyResource} to the xml file
+     * specified by {@code xmlSerializer}.
+     */
+    public void writeToXmlFile(TypedXmlSerializer xmlSerializer) throws IOException {
+        xmlSerializer.attributeInt(/* namespace= */ null, ATTR_RESOURCE_ID, mResourceId);
+        xmlSerializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
+        xmlSerializer.attribute(/* namespace= */ null, ATTR_RESOURCE_NAME, mResourceName);
+        xmlSerializer.attributeInt(/* namespace= */ null, ATTR_RESOURCE_TYPE, mResourceType);
+    }
+
+    /**
+     * Creates a new {@code ParcelableDevicePolicyResource} using the content of
+     * {@code xmlPullParser}.
+     */
+    public static ParcelableResource createFromXml(TypedXmlPullParser xmlPullParser)
+            throws XmlPullParserException, IOException {
+        int resourceId = xmlPullParser.getAttributeInt(/* namespace= */ null, ATTR_RESOURCE_ID);
+        String packageName = xmlPullParser.getAttributeValue(
+                /* namespace= */ null, ATTR_PACKAGE_NAME);
+        String resourceName = xmlPullParser.getAttributeValue(
+                /* namespace= */ null, ATTR_RESOURCE_NAME);
+        int resourceType = xmlPullParser.getAttributeInt(
+                /* namespace= */ null, ATTR_RESOURCE_TYPE);
+
+        return new ParcelableResource(
+                resourceId, packageName, resourceName, resourceType);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ParcelableResource other = (ParcelableResource) o;
+        return mResourceId == other.mResourceId
+                && mPackageName.equals(other.mPackageName)
+                && mResourceName.equals(other.mResourceName)
+                && mResourceType == other.mResourceType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mResourceId, mPackageName, mResourceName, mResourceType);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mResourceId);
+        dest.writeString(mPackageName);
+        dest.writeString(mResourceName);
+        dest.writeInt(mResourceType);
+    }
+
+    public static final @NonNull Creator<ParcelableResource> CREATOR =
+            new Creator<ParcelableResource>() {
+                @Override
+                public ParcelableResource createFromParcel(Parcel in) {
+                    int resourceId = in.readInt();
+                    String packageName = in.readString();
+                    String resourceName = in.readString();
+                    int resourceType = in.readInt();
+
+                    return new ParcelableResource(
+                            resourceId, packageName, resourceName, resourceType);
+                }
+
+                @Override
+                public ParcelableResource[] newArray(int size) {
+                    return new ParcelableResource[size];
+                }
+            };
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 9985cc0..1291766c 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -36,5 +36,5 @@
     boolean isDeviceSecure(int userId);
     boolean isTrustUsuallyManaged(int userId);
     void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
-    void clearAllBiometricRecognized(in BiometricSourceType target);
+    void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 177de83..e214007 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -217,9 +217,9 @@
      * Clears authentication by the specified biometric type for all users.
      */
     @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-    public void clearAllBiometricRecognized(BiometricSourceType source) {
+    public void clearAllBiometricRecognized(BiometricSourceType source, int unlockedUser) {
         try {
-            mService.clearAllBiometricRecognized(source);
+            mService.clearAllBiometricRecognized(source, unlockedUser);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/bluetooth/Attributable.java b/core/java/android/bluetooth/Attributable.java
deleted file mode 100644
index d9acbe3..0000000
--- a/core/java/android/bluetooth/Attributable.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.AttributionSource;
-
-import java.util.List;
-
-/**
- * Marker interface for a class which can have an {@link AttributionSource}
- * assigned to it; these are typically {@link android.os.Parcelable} classes
- * which need to be updated after crossing Binder transaction boundaries.
- *
- * @hide
- */
-public interface Attributable {
-    void setAttributionSource(@NonNull AttributionSource attributionSource);
-
-    static @Nullable <T extends Attributable> T setAttributionSource(
-            @Nullable T attributable,
-            @NonNull AttributionSource attributionSource) {
-        if (attributable != null) {
-            attributable.setAttributionSource(attributionSource);
-        }
-        return attributable;
-    }
-
-    static @Nullable <T extends Attributable> List<T> setAttributionSource(
-            @Nullable List<T> attributableList,
-            @NonNull AttributionSource attributionSource) {
-        if (attributableList != null) {
-            final int size = attributableList.size();
-            for (int i = 0; i < size; i++) {
-                setAttributionSource(attributableList.get(i), attributionSource);
-            }
-        }
-        return attributableList;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
deleted file mode 100644
index 8b9cec1..0000000
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ /dev/null
@@ -1,1149 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-
-/**
- * This class provides the public APIs to control the Bluetooth A2DP
- * profile.
- *
- * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothA2dp proxy object.
- *
- * <p> Android only supports one connected Bluetooth A2dp device at a time.
- * Each method is protected with its appropriate permission.
- */
-public final class BluetoothA2dp implements BluetoothProfile {
-    private static final String TAG = "BluetoothA2dp";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the A2DP
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Playing state of the A2DP
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PLAYING_STATE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Audio Codec state of the
-     * A2DP Source profile.
-     *
-     * <p>This intent will have 2 extras:
-     * <ul>
-     * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
-     * connected, otherwise it is not included.</li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(trackingBug = 181103983)
-    public static final String ACTION_CODEC_CONFIG_CHANGED =
-            "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
-
-    /**
-     * A2DP sink device is streaming music. This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
-     */
-    public static final int STATE_PLAYING = 10;
-
-    /**
-     * A2DP sink device is NOT streaming music. This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
-     */
-    public static final int STATE_NOT_PLAYING = 11;
-
-    /** @hide */
-    @IntDef(prefix = "OPTIONAL_CODECS_", value = {
-            OPTIONAL_CODECS_SUPPORT_UNKNOWN,
-            OPTIONAL_CODECS_NOT_SUPPORTED,
-            OPTIONAL_CODECS_SUPPORTED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface OptionalCodecsSupportStatus {}
-
-    /**
-     * We don't have a stored preference for whether or not the given A2DP sink device supports
-     * optional codecs.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1;
-
-    /**
-     * The given A2DP sink device does not support optional codecs.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0;
-
-    /**
-     * The given A2DP sink device does support optional codecs.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_SUPPORTED = 1;
-
-    /** @hide */
-    @IntDef(prefix = "OPTIONAL_CODECS_PREF_", value = {
-            OPTIONAL_CODECS_PREF_UNKNOWN,
-            OPTIONAL_CODECS_PREF_DISABLED,
-            OPTIONAL_CODECS_PREF_ENABLED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface OptionalCodecsPreferenceStatus {}
-
-    /**
-     * We don't have a stored preference for whether optional codecs should be enabled or
-     * disabled for the given A2DP device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1;
-
-    /**
-     * Optional codecs should be disabled for the given A2DP device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_PREF_DISABLED = 0;
-
-    /**
-     * Optional codecs should be enabled for the given A2DP device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
-
-    /** @hide */
-    @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = {
-            DYNAMIC_BUFFER_SUPPORT_NONE,
-            DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD,
-            DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Type {}
-
-    /**
-     * Indicates the supported type of Dynamic Audio Buffer is not supported.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0;
-
-    /**
-     * Indicates the supported type of Dynamic Audio Buffer is A2DP offload.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1;
-
-    /**
-     * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
-                    IBluetoothA2dp.class.getName()) {
-                @Override
-                public IBluetoothA2dp getServiceInterface(IBinder service) {
-                    return IBluetoothA2dp.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothA2dp proxy object for interacting with the local
-     * Bluetooth A2DP service.
-     */
-    /* package */ BluetoothA2dp(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    @UnsupportedAppUsage
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothA2dp getService() {
-        return mProfileConnector.getService();
-    }
-
-    @Override
-    public void finalize() {
-        // The empty finalize needs to be kept or the
-        // cts signature tests would fail.
-    }
-
-    /**
-     * Initiate connection to a profile of the remote Bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothA2dp service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothA2dp service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStatesWithAttribution(states,
-                        mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @BtProfileState int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, A2DP audio streaming
-     * is to the active A2DP Sink device. If a remote device is not
-     * connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     * @param device the remote Bluetooth device. Could be null to clear
-     * the active device and stop streaming audio to a Bluetooth device.
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) log("setActiveDevice(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected device that is active.
-     *
-     * @return the connected device that is active or null if no device
-     * is active
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getActiveDevice() {
-        if (VDBG) log("getActiveDevice()");
-        final IBluetoothA2dp service = getService();
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevice(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Checks if Avrcp device supports the absolute volume feature.
-     *
-     * @return true if device supports absolute volume
-     * @hide
-     */
-    @RequiresNoPermission
-    public boolean isAvrcpAbsoluteVolumeSupported() {
-        if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isAvrcpAbsoluteVolumeSupported(recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Tells remote device to set an absolute volume. Only if absolute volume is supported
-     *
-     * @param volume Absolute volume to be set on AVRCP side
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setAvrcpAbsoluteVolume(int volume) {
-        if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                service.setAvrcpAbsoluteVolume(volume, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Check if A2DP profile is streaming music.
-     *
-     * @param device BluetoothDevice device
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isA2dpPlaying(BluetoothDevice device) {
-        if (DBG) log("isA2dpPlaying(" + device + ")");
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isA2dpPlaying(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * This function checks if the remote device is an AVCRP
-     * target and thus whether we should send volume keys
-     * changes or not.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean shouldSendVolumeKeys(BluetoothDevice device) {
-        if (isEnabled() && isValidDevice(device)) {
-            ParcelUuid[] uuids = device.getUuids();
-            if (uuids == null) return false;
-
-            for (ParcelUuid uuid : uuids) {
-                if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Gets the current codec status (configuration and capability).
-     *
-     * @param device the remote Bluetooth device.
-     * @return the current codec status
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 181103983)
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
-        verifyDeviceNotNull(device, "getCodecStatus");
-        final IBluetoothA2dp service = getService();
-        final BluetoothCodecStatus defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<BluetoothCodecStatus> recv =
-                        new SynchronousResultReceiver();
-                service.getCodecStatus(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets the codec configuration preference.
-     *
-     * @param device the remote Bluetooth device.
-     * @param codecConfig the codec configuration preference
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 181103983)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setCodecConfigPreference(@NonNull BluetoothDevice device,
-                                         @NonNull BluetoothCodecConfig codecConfig) {
-        if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
-        verifyDeviceNotNull(device, "setCodecConfigPreference");
-        if (codecConfig == null) {
-            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
-            throw new IllegalArgumentException("codecConfig cannot be null");
-        }
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Enables the optional codecs.
-     *
-     * @param device the remote Bluetooth device.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
-        verifyDeviceNotNull(device, "enableOptionalCodecs");
-        enableDisableOptionalCodecs(device, true);
-    }
-
-    /**
-     * Disables the optional codecs.
-     *
-     * @param device the remote Bluetooth device.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
-        verifyDeviceNotNull(device, "disableOptionalCodecs");
-        enableDisableOptionalCodecs(device, false);
-    }
-
-    /**
-     * Enables or disables the optional codecs.
-     *
-     * @param device the remote Bluetooth device.
-     * @param enable if true, enable the optional codecs, other disable them
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                if (enable) {
-                    service.enableOptionalCodecs(device, mAttributionSource);
-                } else {
-                    service.disableOptionalCodecs(device, mAttributionSource);
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Returns whether this device supports optional codecs.
-     *
-     * @param device The device to check
-     * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or
-     * OPTIONAL_CODECS_SUPPORTED.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @OptionalCodecsSupportStatus
-    public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
-        if (DBG) log("isOptionalCodecsSupported(" + device + ")");
-        verifyDeviceNotNull(device, "isOptionalCodecsSupported");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.supportsOptionalCodecs(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns whether this device should have optional codecs enabled.
-     *
-     * @param device The device in question.
-     * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
-     * OPTIONAL_CODECS_PREF_DISABLED.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @OptionalCodecsPreferenceStatus
-    public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
-        if (DBG) log("isOptionalCodecsEnabled(" + device + ")");
-        verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getOptionalCodecsEnabled(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets a persistent preference for whether a given device should have optional codecs enabled.
-     *
-     * @param device The device to set this preference for.
-     * @param value Whether the optional codecs should be enabled for this device.  This should be
-     * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or
-     * OPTIONAL_CODECS_PREF_DISABLED.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
-            @OptionalCodecsPreferenceStatus int value) {
-        if (DBG) log("setOptionalCodecsEnabled(" + device + ")");
-        verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
-        if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
-                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
-                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
-            Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
-            return;
-        }
-        final IBluetoothA2dp service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                service.setOptionalCodecsEnabled(device, value, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Get the supported type of the Dynamic Audio Buffer.
-     * <p>Possible return values are
-     * {@link #DYNAMIC_BUFFER_SUPPORT_NONE},
-     * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
-     * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
-     *
-     * @return supported type of Dynamic Audio Buffer feature
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @Type int getDynamicBufferSupport() {
-        if (VDBG) log("getDynamicBufferSupport()");
-        final IBluetoothA2dp service = getService();
-        final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getDynamicBufferSupport(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Return the record of {@link BufferConstraints} object that
-     * has the default/maximum/minimum audio buffer. This can be used to inform what the controller
-     * has support for the audio buffer.
-     *
-     * @return a record with {@link BufferConstraints} or null if report is unavailable
-     * or unsupported
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @Nullable BufferConstraints getBufferConstraints() {
-        if (VDBG) log("getBufferConstraints()");
-        final IBluetoothA2dp service = getService();
-        final BufferConstraints defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BufferConstraints> recv =
-                        new SynchronousResultReceiver();
-                service.getBufferConstraints(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set Dynamic Audio Buffer Size.
-     *
-     * @param codec audio codec
-     * @param value buffer millis
-     * @return true to indicate success, or false on immediate error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
-            int value) {
-        if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
-        if (value < 0) {
-            Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
-            return false;
-        }
-        final IBluetoothA2dp service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setBufferLengthMillis(codec, value, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            case STATE_PLAYING:
-                return "playing";
-            case STATE_NOT_PLAYING:
-                return "not playing";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
-        if (device == null) {
-            Log.e(TAG, methodName + ": device param is null");
-            throw new IllegalArgumentException("Device cannot be null");
-        }
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
deleted file mode 100755
index 5941681..0000000
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth A2DP Sink
- * profile.
- *
- * <p>BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothA2dpSink proxy object.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothA2dpSink implements BluetoothProfile {
-    private static final String TAG = "BluetoothA2dpSink";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the A2DP Sink
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @SystemApi
-    @SuppressLint("ActionValue")
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK,
-                    "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
-                @Override
-                public IBluetoothA2dpSink getServiceInterface(IBinder service) {
-                    return IBluetoothA2dpSink.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothA2dp proxy object for interacting with the local
-     * Bluetooth A2DP service.
-     */
-    /* package */ BluetoothA2dpSink(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothA2dpSink getService() {
-        return mProfileConnector.getService();
-    }
-
-    @Override
-    public void finalize() {
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> Currently, the system supports only 1 connection to the
-     * A2DP profile. The API will automatically disconnect connected
-     * devices before connecting.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothA2dpSink service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothA2dpSink service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the current audio configuration for the A2DP source device,
-     * or null if the device has no audio configuration
-     *
-     * @param device Remote bluetooth device.
-     * @return audio configuration for the device, or null
-     *
-     * {@see BluetoothAudioConfig}
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
-        if (VDBG) log("getAudioConfig(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final BluetoothAudioConfig defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<BluetoothAudioConfig> recv =
-                        new SynchronousResultReceiver();
-                service.getAudioConfig(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if audio is playing on the bluetooth device (A2DP profile is streaming music).
-     *
-     * @param device BluetoothDevice device
-     * @return true if audio is playing (A2dp is streaming music), false otherwise
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
-        if (VDBG) log("isAudioPlaying(" + device + ")");
-        final IBluetoothA2dpSink service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isA2dpPlaying(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            case BluetoothA2dp.STATE_PLAYING:
-                return "playing";
-            case BluetoothA2dp.STATE_NOT_PLAYING:
-                return "not playing";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
deleted file mode 100644
index c17a7b4..0000000
--- a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import android.annotation.ElapsedRealtimeLong;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Record of energy and activity information from controller and
- * underlying bt stack state.Timestamp the record with system
- * time.
- *
- * @hide
- */
-@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
-public final class BluetoothActivityEnergyInfo implements Parcelable {
-    private final long mTimestamp;
-    private int mBluetoothStackState;
-    private long mControllerTxTimeMs;
-    private long mControllerRxTimeMs;
-    private long mControllerIdleTimeMs;
-    private long mControllerEnergyUsed;
-    private List<UidTraffic> mUidTraffic;
-
-    /** @hide */
-    @IntDef(prefix = { "BT_STACK_STATE_" }, value = {
-            BT_STACK_STATE_INVALID,
-            BT_STACK_STATE_STATE_ACTIVE,
-            BT_STACK_STATE_STATE_SCANNING,
-            BT_STACK_STATE_STATE_IDLE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BluetoothStackState {}
-
-    public static final int BT_STACK_STATE_INVALID = 0;
-    public static final int BT_STACK_STATE_STATE_ACTIVE = 1;
-    public static final int BT_STACK_STATE_STATE_SCANNING = 2;
-    public static final int BT_STACK_STATE_STATE_IDLE = 3;
-
-    /** @hide */
-    public BluetoothActivityEnergyInfo(long timestamp, int stackState,
-            long txTime, long rxTime, long idleTime, long energyUsed) {
-        mTimestamp = timestamp;
-        mBluetoothStackState = stackState;
-        mControllerTxTimeMs = txTime;
-        mControllerRxTimeMs = rxTime;
-        mControllerIdleTimeMs = idleTime;
-        mControllerEnergyUsed = energyUsed;
-    }
-
-    /** @hide */
-    private BluetoothActivityEnergyInfo(Parcel in) {
-        mTimestamp = in.readLong();
-        mBluetoothStackState = in.readInt();
-        mControllerTxTimeMs = in.readLong();
-        mControllerRxTimeMs = in.readLong();
-        mControllerIdleTimeMs = in.readLong();
-        mControllerEnergyUsed = in.readLong();
-        mUidTraffic = in.createTypedArrayList(UidTraffic.CREATOR);
-    }
-
-    /** @hide */
-    @Override
-    public String toString() {
-        return "BluetoothActivityEnergyInfo{"
-                + " mTimestamp=" + mTimestamp
-                + " mBluetoothStackState=" + mBluetoothStackState
-                + " mControllerTxTimeMs=" + mControllerTxTimeMs
-                + " mControllerRxTimeMs=" + mControllerRxTimeMs
-                + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
-                + " mControllerEnergyUsed=" + mControllerEnergyUsed
-                + " mUidTraffic=" + mUidTraffic
-                + " }";
-    }
-
-    public static final @NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
-            new Parcelable.Creator<BluetoothActivityEnergyInfo>() {
-                public BluetoothActivityEnergyInfo createFromParcel(Parcel in) {
-                    return new BluetoothActivityEnergyInfo(in);
-                }
-
-                public BluetoothActivityEnergyInfo[] newArray(int size) {
-                    return new BluetoothActivityEnergyInfo[size];
-                }
-            };
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mTimestamp);
-        out.writeInt(mBluetoothStackState);
-        out.writeLong(mControllerTxTimeMs);
-        out.writeLong(mControllerRxTimeMs);
-        out.writeLong(mControllerIdleTimeMs);
-        out.writeLong(mControllerEnergyUsed);
-        out.writeTypedList(mUidTraffic);
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Get the Bluetooth stack state associated with the energy info.
-     *
-     * @return one of {@link #BluetoothStackState} states
-     */
-    @BluetoothStackState
-    public int getBluetoothStackState() {
-        return mBluetoothStackState;
-    }
-
-    /**
-     * @return tx time in ms
-     */
-    public long getControllerTxTimeMillis() {
-        return mControllerTxTimeMs;
-    }
-
-    /**
-     * @return rx time in ms
-     */
-    public long getControllerRxTimeMillis() {
-        return mControllerRxTimeMs;
-    }
-
-    /**
-     * @return idle time in ms
-     */
-    public long getControllerIdleTimeMillis() {
-        return mControllerIdleTimeMs;
-    }
-
-    /**
-     * Get the product of current (mA), voltage (V), and time (ms).
-     *
-     * @return energy used
-     */
-    public long getControllerEnergyUsed() {
-        return mControllerEnergyUsed;
-    }
-
-    /**
-     * @return timestamp (real time elapsed in milliseconds since boot) of record creation
-     */
-    public @ElapsedRealtimeLong long getTimestampMillis() {
-        return mTimestamp;
-    }
-
-    /**
-     * Get the {@link List} of each application {@link android.bluetooth.UidTraffic}.
-     *
-     * @return current {@link List} of {@link android.bluetooth.UidTraffic}
-     */
-    public @NonNull List<UidTraffic> getUidTraffic() {
-        if (mUidTraffic == null) {
-            return Collections.emptyList();
-        }
-        return mUidTraffic;
-    }
-
-    /** @hide */
-    public void setUidTraffic(List<UidTraffic> traffic) {
-        mUidTraffic = traffic;
-    }
-
-    /**
-     * @return true if the record Tx time, Rx time, and Idle time are more than 0.
-     */
-    public boolean isValid() {
-        return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0)
-                && (mControllerIdleTimeMs >= 0));
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
deleted file mode 100644
index a695f6d..0000000
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ /dev/null
@@ -1,4375 +0,0 @@
-/*
- * Copyright 2009-2016 The Android Open Source Project
- * Copyright 2015 Samsung LSI
- *
- * 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.bluetooth;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
-import android.bluetooth.BluetoothDevice.Transport;
-import android.bluetooth.BluetoothProfile.ConnectionPolicy;
-import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.bluetooth.le.BluetoothLeAdvertiser;
-import android.bluetooth.le.BluetoothLeScanner;
-import android.bluetooth.le.PeriodicAdvertisingManager;
-import android.bluetooth.le.ScanCallback;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanRecord;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.le.ScanSettings;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.sysprop.BluetoothProperties;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-import java.util.WeakHashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
- * lets you perform fundamental Bluetooth tasks, such as initiate
- * device discovery, query a list of bonded (paired) devices,
- * instantiate a {@link BluetoothDevice} using a known MAC address, and create
- * a {@link BluetoothServerSocket} to listen for connection requests from other
- * devices, and start a scan for Bluetooth LE devices.
- *
- * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
- * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}.
- * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter}
- * method instead.
- * </p><p>
- * Fundamentally, this is your starting point for all
- * Bluetooth actions. Once you have the local adapter, you can get a set of
- * {@link BluetoothDevice} objects representing all paired devices with
- * {@link #getBondedDevices()}; start device discovery with
- * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
- * listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
- * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
- * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
- * </p>
- * <p>This class is thread safe.</p>
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about using Bluetooth, read the <a href=
- * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
- * guide.
- * </p>
- * </div>
- *
- * {@see BluetoothDevice}
- * {@see BluetoothServerSocket}
- */
-public final class BluetoothAdapter {
-    private static final String TAG = "BluetoothAdapter";
-    private static final String DESCRIPTOR = "android.bluetooth.BluetoothAdapter";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Default MAC address reported to a client that does not have the
-     * android.permission.LOCAL_MAC_ADDRESS permission.
-     *
-     * @hide
-     */
-    public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
-
-    /**
-     * Sentinel error value for this class. Guaranteed to not equal any other
-     * integer constant in this class. Provided as a convenience for functions
-     * that require a sentinel error value, for example:
-     * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-     * BluetoothAdapter.ERROR)</code>
-     */
-    public static final int ERROR = Integer.MIN_VALUE;
-
-    /**
-     * Broadcast Action: The state of the local Bluetooth adapter has been
-     * changed.
-     * <p>For example, Bluetooth has been turned on or off.
-     * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
-     * #EXTRA_PREVIOUS_STATE} containing the new and old states
-     * respectively.
-     */
-    @RequiresLegacyBluetoothPermission
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
-     * intents to request the current power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     */
-    public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE";
-    /**
-     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
-     * intents to request the previous power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF}
-     */
-    public static final String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.adapter.extra.PREVIOUS_STATE";
-
-    /** @hide */
-    @IntDef(prefix = { "STATE_" }, value = {
-            STATE_OFF,
-            STATE_TURNING_ON,
-            STATE_ON,
-            STATE_TURNING_OFF,
-            STATE_BLE_TURNING_ON,
-            STATE_BLE_ON,
-            STATE_BLE_TURNING_OFF
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AdapterState {}
-
-    /**
-     * Indicates the local Bluetooth adapter is off.
-     */
-    public static final int STATE_OFF = 10;
-    /**
-     * Indicates the local Bluetooth adapter is turning on. However local
-     * clients should wait for {@link #STATE_ON} before attempting to
-     * use the adapter.
-     */
-    public static final int STATE_TURNING_ON = 11;
-    /**
-     * Indicates the local Bluetooth adapter is on, and ready for use.
-     */
-    public static final int STATE_ON = 12;
-    /**
-     * Indicates the local Bluetooth adapter is turning off. Local clients
-     * should immediately attempt graceful disconnection of any remote links.
-     */
-    public static final int STATE_TURNING_OFF = 13;
-
-    /**
-     * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
-     *
-     * @hide
-     */
-    public static final int STATE_BLE_TURNING_ON = 14;
-
-    /**
-     * Indicates the local Bluetooth adapter is in LE only mode.
-     *
-     * @hide
-     */
-    public static final int STATE_BLE_ON = 15;
-
-    /**
-     * Indicates the local Bluetooth adapter is turning off LE only mode.
-     *
-     * @hide
-     */
-    public static final int STATE_BLE_TURNING_OFF = 16;
-
-    /**
-     * UUID of the GATT Read Characteristics for LE_PSM value.
-     *
-     * @hide
-     */
-    public static final UUID LE_PSM_CHARACTERISTIC_UUID =
-            UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
-
-    /**
-     * Human-readable string helper for AdapterState
-     *
-     * @hide
-     */
-    public static String nameForState(@AdapterState int state) {
-        switch (state) {
-            case STATE_OFF:
-                return "OFF";
-            case STATE_TURNING_ON:
-                return "TURNING_ON";
-            case STATE_ON:
-                return "ON";
-            case STATE_TURNING_OFF:
-                return "TURNING_OFF";
-            case STATE_BLE_TURNING_ON:
-                return "BLE_TURNING_ON";
-            case STATE_BLE_ON:
-                return "BLE_ON";
-            case STATE_BLE_TURNING_OFF:
-                return "BLE_TURNING_OFF";
-            default:
-                return "?!?!? (" + state + ")";
-        }
-    }
-
-    /**
-     * Activity Action: Show a system activity that requests discoverable mode.
-     * This activity will also request the user to turn on Bluetooth if it
-     * is not currently enabled.
-     * <p>Discoverable mode is equivalent to {@link
-     * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
-     * this Bluetooth adapter when they perform a discovery.
-     * <p>For privacy, Android is not discoverable by default.
-     * <p>The sender of this Intent can optionally use extra field {@link
-     * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
-     * discoverability. Currently the default duration is 120 seconds, and
-     * maximum duration is capped at 300 seconds for each request.
-     * <p>Notification of the result of this activity is posted using the
-     * {@link android.app.Activity#onActivityResult} callback. The
-     * <code>resultCode</code>
-     * will be the duration (in seconds) of discoverability or
-     * {@link android.app.Activity#RESULT_CANCELED} if the user rejected
-     * discoverability or an error has occurred.
-     * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
-     * for global notification whenever the scan mode changes. For example, an
-     * application can be notified when the device has ended discoverability.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
-            ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
-
-    /**
-     * Used as an optional int extra field in {@link
-     * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
-     * for discoverability in seconds. The current default is 120 seconds, and
-     * requests over 300 seconds will be capped. These values could change.
-     */
-    public static final String EXTRA_DISCOVERABLE_DURATION =
-            "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
-
-    /**
-     * Activity Action: Show a system activity that allows the user to turn on
-     * Bluetooth.
-     * <p>This system activity will return once Bluetooth has completed turning
-     * on, or the user has decided not to turn Bluetooth on.
-     * <p>Notification of the result of this activity is posted using the
-     * {@link android.app.Activity#onActivityResult} callback. The
-     * <code>resultCode</code>
-     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
-     * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user
-     * has rejected the request or an error has occurred.
-     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
-     * for global notification whenever Bluetooth is turned on or off.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
-            ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
-
-    /**
-     * Activity Action: Show a system activity that allows the user to turn off
-     * Bluetooth. This is used only if permission review is enabled which is for
-     * apps targeting API less than 23 require a permission review before any of
-     * the app's components can run.
-     * <p>This system activity will return once Bluetooth has completed turning
-     * off, or the user has decided not to turn Bluetooth off.
-     * <p>Notification of the result of this activity is posted using the
-     * {@link android.app.Activity#onActivityResult} callback. The
-     * <code>resultCode</code>
-     * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
-     * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user
-     * has rejected the request or an error has occurred.
-     * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
-     * for global notification whenever Bluetooth is turned on or off.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
-            ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
-
-    /**
-     * Activity Action: Show a system activity that allows user to enable BLE scans even when
-     * Bluetooth is turned off.<p>
-     *
-     * Notification of result of this activity is posted using
-     * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be
-     * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or
-     * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an
-     * error occurred.
-     *
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE =
-            "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
-
-    /**
-     * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
-     * has changed.
-     * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
-     * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
-     * respectively.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
-     * intents to request the current scan mode. Possible values are:
-     * {@link #SCAN_MODE_NONE},
-     * {@link #SCAN_MODE_CONNECTABLE},
-     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
-     */
-    public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
-    /**
-     * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
-     * intents to request the previous scan mode. Possible values are:
-     * {@link #SCAN_MODE_NONE},
-     * {@link #SCAN_MODE_CONNECTABLE},
-     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
-     */
-    public static final String EXTRA_PREVIOUS_SCAN_MODE =
-            "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
-
-    /** @hide */
-    @IntDef(prefix = { "SCAN_" }, value = {
-            SCAN_MODE_NONE,
-            SCAN_MODE_CONNECTABLE,
-            SCAN_MODE_CONNECTABLE_DISCOVERABLE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanMode {}
-
-    /** @hide */
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanModeStatusCode {}
-
-    /**
-     * Indicates that both inquiry scan and page scan are disabled on the local
-     * Bluetooth adapter. Therefore this device is neither discoverable
-     * nor connectable from remote Bluetooth devices.
-     */
-    public static final int SCAN_MODE_NONE = 20;
-    /**
-     * Indicates that inquiry scan is disabled, but page scan is enabled on the
-     * local Bluetooth adapter. Therefore this device is not discoverable from
-     * remote Bluetooth devices, but is connectable from remote devices that
-     * have previously discovered this device.
-     */
-    public static final int SCAN_MODE_CONNECTABLE = 21;
-    /**
-     * Indicates that both inquiry scan and page scan are enabled on the local
-     * Bluetooth adapter. Therefore this device is both discoverable and
-     * connectable from remote Bluetooth devices.
-     */
-    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
-
-    /**
-     * Device only has a display.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_OUT = 0;
-
-    /**
-     * Device has a display and the ability to input Yes/No.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_IO = 1;
-
-    /**
-     * Device only has a keyboard for entry but no display.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_IN = 2;
-
-    /**
-     * Device has no Input or Output capability.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_NONE = 3;
-
-    /**
-     * Device has a display and a full keyboard.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_KBDISP = 4;
-
-    /**
-     * Maximum range value for Input/Output capabilities.
-     *
-     * <p>This should be updated when adding a new Input/Output capability. Other code
-     * like validation depends on this being accurate.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_MAX = 5;
-
-    /**
-     * The Input/Output capability of the device is unknown.
-     *
-     * @hide
-     */
-    public static final int IO_CAPABILITY_UNKNOWN = 255;
-
-    /** @hide */
-    @IntDef({IO_CAPABILITY_OUT, IO_CAPABILITY_IO, IO_CAPABILITY_IN, IO_CAPABILITY_NONE,
-            IO_CAPABILITY_KBDISP})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IoCapability {}
-
-    /** @hide */
-    @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO,
-            ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ActiveDeviceUse {}
-
-    /**
-     * Use the specified device for audio (a2dp and hearing aid profile)
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACTIVE_DEVICE_AUDIO = 0;
-
-    /**
-     * Use the specified device for phone calls (headset profile and hearing
-     * aid profile)
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACTIVE_DEVICE_PHONE_CALL = 1;
-
-    /**
-     * Use the specified device for a2dp, hearing aid profile, and headset profile
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACTIVE_DEVICE_ALL = 2;
-
-    /** @hide */
-    @IntDef({BluetoothProfile.HEADSET, BluetoothProfile.A2DP,
-            BluetoothProfile.HEARING_AID})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ActiveDeviceProfile {}
-
-    /**
-     * Broadcast Action: The local Bluetooth adapter has started the remote
-     * device discovery process.
-     * <p>This usually involves an inquiry scan of about 12 seconds, followed
-     * by a page scan of each new device to retrieve its Bluetooth name.
-     * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
-     * remote Bluetooth devices are found.
-     * <p>Device discovery is a heavyweight procedure. New connections to
-     * remote Bluetooth devices should not be attempted while discovery is in
-     * progress, and existing connections will experience limited bandwidth
-     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
-     * discovery.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
-    /**
-     * Broadcast Action: The local Bluetooth adapter has finished the device
-     * discovery process.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
-
-    /**
-     * Broadcast Action: The local Bluetooth adapter has changed its friendly
-     * Bluetooth name.
-     * <p>This name is visible to remote Bluetooth devices.
-     * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
-     * the name.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
-    /**
-     * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
-     * intents to request the local Bluetooth name.
-     */
-    public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
-
-    /**
-     * Intent used to broadcast the change in connection state of the local
-     * Bluetooth adapter to a profile of the remote device. When the adapter is
-     * not connected to any profiles of any remote devices and it attempts a
-     * connection to a profile this intent will be sent. Once connected, this intent
-     * will not be sent for any more connection attempts to any profiles of any
-     * remote device. When the adapter disconnects from the last profile its
-     * connected to of any remote device, this intent will be sent.
-     *
-     * <p> This intent is useful for applications that are only concerned about
-     * whether the local adapter is connected to any profile of any device and
-     * are not really concerned about which profile. For example, an application
-     * which displays an icon to display whether Bluetooth is connected or not
-     * can use this intent.
-     *
-     * <p>This intent will have 3 extras:
-     * {@link #EXTRA_CONNECTION_STATE} - The current connection state.
-     * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state.
-     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
-     *
-     * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE}
-     * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
-            ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * This extra represents the current connection state.
-     */
-    public static final String EXTRA_CONNECTION_STATE =
-            "android.bluetooth.adapter.extra.CONNECTION_STATE";
-
-    /**
-     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * This extra represents the previous connection state.
-     */
-    public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
-            "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
-
-    /**
-     * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi public static final String ACTION_BLE_STATE_CHANGED =
-            "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Bluetooth address
-     * of the local Bluetooth adapter.
-     * <p>Always contains the extra field {@link
-     * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address.
-     *
-     * Note: only system level processes are allowed to send this
-     * defined broadcast.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED =
-            "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED";
-
-    /**
-     * Used as a String extra field in {@link
-     * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local
-     * Bluetooth address.
-     *
-     * @hide
-     */
-    public static final String EXTRA_BLUETOOTH_ADDRESS =
-            "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS";
-
-    /**
-     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
-     * by BLE Always on enabled application to know the ACL_CONNECTED event
-     * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
-     * as Bluetooth LE is the only feature available in STATE_BLE_ON
-     *
-     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
-     * works in Bluetooth state STATE_ON
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLE_ACL_CONNECTED =
-            "android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
-
-    /**
-     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
-     * by BLE Always on enabled application to know the ACL_DISCONNECTED event
-     * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
-     * LE is the only feature available in STATE_BLE_ON
-     *
-     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
-     * works in Bluetooth state STATE_ON
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLE_ACL_DISCONNECTED =
-            "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
-
-    /** The profile is in disconnected state */
-    public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
-    /** The profile is in connecting state */
-    public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
-    /** The profile is in connected state */
-    public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
-    /** The profile is in disconnecting state */
-    public static final int STATE_DISCONNECTING =
-            BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
-
-    /** @hide */
-    public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
-    private final IBinder mToken;
-
-
-    /**
-     * When creating a ServerSocket using listenUsingRfcommOn() or
-     * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create
-     * a ServerSocket that auto assigns a channel number to the first
-     * bluetooth socket.
-     * The channel number assigned to this first Bluetooth Socket will
-     * be stored in the ServerSocket, and reused for subsequent Bluetooth
-     * sockets.
-     *
-     * @hide
-     */
-    public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2;
-
-
-    private static final int ADDRESS_LENGTH = 17;
-
-    /**
-     * Lazily initialized singleton. Guaranteed final after first object
-     * constructed.
-     */
-    private static BluetoothAdapter sAdapter;
-
-    private BluetoothLeScanner mBluetoothLeScanner;
-    private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
-    private PeriodicAdvertisingManager mPeriodicAdvertisingManager;
-
-    private final IBluetoothManager mManagerService;
-    private final AttributionSource mAttributionSource;
-
-    // Yeah, keeping both mService and sService isn't pretty, but it's too late
-    // in the current release for a major refactoring, so we leave them both
-    // intact until this can be cleaned up in a future release
-
-    @UnsupportedAppUsage
-    @GuardedBy("mServiceLock")
-    private IBluetooth mService;
-    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
-
-    @GuardedBy("sServiceLock")
-    private static boolean sServiceRegistered;
-    @GuardedBy("sServiceLock")
-    private static IBluetooth sService;
-    private static final Object sServiceLock = new Object();
-
-    private final Object mLock = new Object();
-    private final Map<LeScanCallback, ScanCallback> mLeScanClients;
-    private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
-                mMetadataListeners = new HashMap<>();
-    private final Map<BluetoothConnectionCallback, Executor>
-            mBluetoothConnectionCallbackExecutorMap = new HashMap<>();
-
-    /**
-     * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
-     * implementation.
-     */
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothMetadataListener mBluetoothMetadataListener =
-            new IBluetoothMetadataListener.Stub() {
-        @Override
-        public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            synchronized (mMetadataListeners) {
-                if (mMetadataListeners.containsKey(device)) {
-                    List<Pair<OnMetadataChangedListener, Executor>> list =
-                            mMetadataListeners.get(device);
-                    for (Pair<OnMetadataChangedListener, Executor> pair : list) {
-                        OnMetadataChangedListener listener = pair.first;
-                        Executor executor = pair.second;
-                        executor.execute(() -> {
-                            listener.onMetadataChanged(device, key, value);
-                        });
-                    }
-                }
-            }
-            return;
-        }
-    };
-
-    /**
-     * Get a handle to the default local Bluetooth adapter.
-     * <p>
-     * Currently Android only supports one Bluetooth adapter, but the API could
-     * be extended to support more. This will always return the default adapter.
-     * </p>
-     *
-     * @return the default local adapter, or null if Bluetooth is not supported
-     *         on this hardware platform
-     * @deprecated this method will continue to work, but developers are
-     *             strongly encouraged to migrate to using
-     *             {@link BluetoothManager#getAdapter()}, since that approach
-     *             enables support for {@link Context#createAttributionContext}.
-     */
-    @Deprecated
-    @RequiresNoPermission
-    public static synchronized BluetoothAdapter getDefaultAdapter() {
-        if (sAdapter == null) {
-            sAdapter = createAdapter(AttributionSource.myAttributionSource());
-        }
-        return sAdapter;
-    }
-
-    /** {@hide} */
-    public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
-        IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
-        if (binder != null) {
-            return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder),
-                    attributionSource);
-        } else {
-            Log.e(TAG, "Bluetooth binder is null");
-            return null;
-        }
-    }
-
-    /**
-     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
-     */
-    BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
-        mManagerService = Objects.requireNonNull(managerService);
-        mAttributionSource = Objects.requireNonNull(attributionSource);
-        synchronized (mServiceLock.writeLock()) {
-            mService = getBluetoothService(mManagerCallback);
-        }
-        mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
-        mToken = new Binder(DESCRIPTOR);
-    }
-
-    /**
-     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
-     * address.
-     * <p>Valid Bluetooth hardware addresses must be upper case, in a format
-     * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
-     * available to validate a Bluetooth address.
-     * <p>A {@link BluetoothDevice} will always be returned for a valid
-     * hardware address, even if this adapter has never seen that device.
-     *
-     * @param address valid Bluetooth MAC address
-     * @throws IllegalArgumentException if address is invalid
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getRemoteDevice(String address) {
-        final BluetoothDevice res = new BluetoothDevice(address);
-        res.setAttributionSource(mAttributionSource);
-        return res;
-    }
-
-    /**
-     * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
-     * address.
-     * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method
-     * expects the address in network byte order (MSB first).
-     * <p>A {@link BluetoothDevice} will always be returned for a valid
-     * hardware address, even if this adapter has never seen that device.
-     *
-     * @param address Bluetooth MAC address (6 bytes)
-     * @throws IllegalArgumentException if address is invalid
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getRemoteDevice(byte[] address) {
-        if (address == null || address.length != 6) {
-            throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
-        }
-        final BluetoothDevice res = new BluetoothDevice(
-                String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
-                        address[2], address[3], address[4], address[5]));
-        res.setAttributionSource(mAttributionSource);
-        return res;
-    }
-
-    /**
-     * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
-     * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not
-     * supported on this device.
-     * <p>
-     * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
-     * on this device before calling this method.
-     */
-    @RequiresNoPermission
-    public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
-        if (!getLeAccess()) {
-            return null;
-        }
-        synchronized (mLock) {
-            if (mBluetoothLeAdvertiser == null) {
-                mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this);
-            }
-            return mBluetoothLeAdvertiser;
-        }
-    }
-
-    /**
-     * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising
-     * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic
-     * Advertising is not supported on this device.
-     * <p>
-     * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is
-     * supported on this device before calling this method.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
-        if (!getLeAccess()) {
-            return null;
-        }
-
-        if (!isLePeriodicAdvertisingSupported()) {
-            return null;
-        }
-
-        synchronized (mLock) {
-            if (mPeriodicAdvertisingManager == null) {
-                mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this);
-            }
-            return mPeriodicAdvertisingManager;
-        }
-    }
-
-    /**
-     * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
-     */
-    @RequiresNoPermission
-    public BluetoothLeScanner getBluetoothLeScanner() {
-        if (!getLeAccess()) {
-            return null;
-        }
-        synchronized (mLock) {
-            if (mBluetoothLeScanner == null) {
-                mBluetoothLeScanner = new BluetoothLeScanner(this);
-            }
-            return mBluetoothLeScanner;
-        }
-    }
-
-    /**
-     * Return true if Bluetooth is currently enabled and ready for use.
-     * <p>Equivalent to:
-     * <code>getBluetoothState() == STATE_ON</code>
-     *
-     * @return true if the local adapter is turned on
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isEnabled() {
-        return getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    /**
-     * Return true if Bluetooth LE(Always BLE On feature) is currently
-     * enabled and ready for use
-     * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
-     *
-     * @return true if the local Bluetooth LE adapter is turned on
-     * @hide
-     */
-    @SystemApi
-    @RequiresNoPermission
-    public boolean isLeEnabled() {
-        final int state = getLeState();
-        if (DBG) {
-            Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
-        }
-        return (state == BluetoothAdapter.STATE_ON
-                || state == BluetoothAdapter.STATE_BLE_ON
-                || state == BluetoothAdapter.STATE_TURNING_ON
-                || state == BluetoothAdapter.STATE_TURNING_OFF);
-    }
-
-    /**
-     * Turns off Bluetooth LE which was earlier turned on by calling enableBLE().
-     *
-     * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
-     * to STATE_OFF and completely shut-down Bluetooth
-     *
-     * <p> If the Adapter state is STATE_ON, This would unregister the existance of
-     * special Bluetooth LE application and hence the further turning off of Bluetooth
-     * from UI would ensure the complete turn-off of Bluetooth rather than staying back
-     * BLE only state
-     *
-     * <p>This is an asynchronous call: it will return immediately, and
-     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
-     * later transition to either {@link #STATE_BLE_ON} or {@link
-     * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
-     * If this call returns false then there was an
-     * immediate problem that will prevent the QAdapter from being turned off -
-     * such as the QAadapter already being turned off.
-     *
-     * @return true to indicate success, or false on immediate error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disableBLE() {
-        if (!isBleScanAlwaysAvailable()) {
-            return false;
-        }
-        try {
-            return mManagerService.disableBle(mAttributionSource, mToken);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE.
-     *
-     * enableBLE registers the existence of an app using only LE functions.
-     *
-     * enableBLE may enable Bluetooth to an LE only mode so that an app can use
-     * LE related features (BluetoothGatt or BluetoothGattServer classes)
-     *
-     * If the user disables Bluetooth while an app is registered to use LE only features,
-     * Bluetooth will remain on in LE only mode for the app.
-     *
-     * When Bluetooth is in LE only mode, it is not shown as ON to the UI.
-     *
-     * <p>This is an asynchronous call: it returns immediately, and
-     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
-     * to be notified of adapter state changes.
-     *
-     * If this call returns * true, then the adapter state is either in a mode where
-     * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON},
-     * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}.
-     *
-     * If this call returns false then there was an immediate problem that prevents the
-     * adapter from being turned on - such as Airplane mode.
-     *
-     * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various
-     * states, It includes all the classic Bluetooth Adapter states along with
-     * internal BLE only states
-     *
-     * @return true to indicate Bluetooth LE will be available, or false on immediate error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enableBLE() {
-        if (!isBleScanAlwaysAvailable()) {
-            return false;
-        }
-        try {
-            return mManagerService.enableBle(mAttributionSource, mToken);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-
-        return false;
-    }
-
-    private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state";
-
-    private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache =
-            new PropertyInvalidatedCache<Void, Integer>(
-                8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public Integer recompute(Void query) {
-                    try {
-                        return mService.getState();
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
-                    }
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableBluetoothGetStateCache() {
-        mBluetoothGetStateCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateBluetoothGetStateCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY);
-    }
-
-    /**
-     * Fetch the current bluetooth state.  If the service is down, return
-     * OFF.
-     */
-    @AdapterState
-    private int getStateInternal() {
-        int state = BluetoothAdapter.STATE_OFF;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                state = mBluetoothGetStateCache.query(null);
-            }
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "", e.getCause());
-            } else {
-                throw e;
-            }
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return state;
-    }
-
-    /**
-     * Get the current state of the local Bluetooth adapter.
-     * <p>Possible return values are
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF}.
-     *
-     * @return current state of Bluetooth adapter
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    @AdapterState
-    public int getState() {
-        int state = getStateInternal();
-
-        // Consider all internal states as OFF
-        if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
-                || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            if (VDBG) {
-                Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
-            }
-            state = BluetoothAdapter.STATE_OFF;
-        }
-        if (VDBG) {
-            Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
-                    state));
-        }
-        return state;
-    }
-
-    /**
-     * Get the current state of the local Bluetooth adapter
-     * <p>This returns current internal state of Adapter including LE ON/OFF
-     *
-     * <p>Possible return values are
-     * {@link #STATE_OFF},
-     * {@link #STATE_BLE_TURNING_ON},
-     * {@link #STATE_BLE_ON},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     * {@link #STATE_BLE_TURNING_OFF}.
-     *
-     * @return current state of Bluetooth adapter
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    @AdapterState
-    @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
-            + "whether you can use BLE & BT classic.")
-    public int getLeState() {
-        int state = getStateInternal();
-
-        if (VDBG) {
-            Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
-        }
-        return state;
-    }
-
-    boolean getLeAccess() {
-        if (getLeState() == STATE_ON) {
-            return true;
-        } else if (getLeState() == STATE_BLE_ON) {
-            return true; // TODO: FILTER SYSTEM APPS HERE <--
-        }
-
-        return false;
-    }
-
-    /**
-     * Turn on the local Bluetooth adapter&mdash;do not use without explicit
-     * user action to turn on Bluetooth.
-     * <p>This powers on the underlying Bluetooth hardware, and starts all
-     * Bluetooth system services.
-     * <p class="caution"><strong>Bluetooth should never be enabled without
-     * direct user consent</strong>. If you want to turn on Bluetooth in order
-     * to create a wireless connection, you should use the {@link
-     * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests
-     * user permission to turn on Bluetooth. The {@link #enable()} method is
-     * provided only for applications that include a user interface for changing
-     * system settings, such as a "power manager" app.</p>
-     * <p>This is an asynchronous call: it will return immediately, and
-     * clients should listen for {@link #ACTION_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes. If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
-     * later transition to either {@link #STATE_OFF} or {@link
-     * #STATE_ON}. If this call returns false then there was an
-     * immediate problem that will prevent the adapter from being turned on -
-     * such as Airplane mode, or the adapter is already turned on.
-     *
-     * @return true to indicate adapter startup has begun, or false on immediate error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enable() {
-        if (isEnabled()) {
-            if (DBG) {
-                Log.d(TAG, "enable(): BT already enabled!");
-            }
-            return true;
-        }
-        try {
-            return mManagerService.enable(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Turn off the local Bluetooth adapter&mdash;do not use without explicit
-     * user action to turn off Bluetooth.
-     * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
-     * system services, and powers down the underlying Bluetooth hardware.
-     * <p class="caution"><strong>Bluetooth should never be disabled without
-     * direct user consent</strong>. The {@link #disable()} method is
-     * provided only for applications that include a user interface for changing
-     * system settings, such as a "power manager" app.</p>
-     * <p>This is an asynchronous call: it will return immediately, and
-     * clients should listen for {@link #ACTION_STATE_CHANGED}
-     * to be notified of subsequent adapter state changes. If this call returns
-     * true, then the adapter state will immediately transition from {@link
-     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
-     * later transition to either {@link #STATE_OFF} or {@link
-     * #STATE_ON}. If this call returns false then there was an
-     * immediate problem that will prevent the adapter from being turned off -
-     * such as the adapter already being turned off.
-     *
-     * @return true to indicate adapter shutdown has begun, or false on immediate error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disable() {
-        try {
-            return mManagerService.disable(mAttributionSource, true);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Turn off the local Bluetooth adapter and don't persist the setting.
-     *
-     * @param persist Indicate whether the off state should be persisted following the next reboot
-     * @return true to indicate adapter shutdown has begun, or false on immediate error
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disable(boolean persist) {
-
-        try {
-            return mManagerService.disable(mAttributionSource, persist);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Returns the hardware address of the local Bluetooth adapter.
-     * <p>For example, "00:11:22:AA:BB:CC".
-     *
-     * @return Bluetooth hardware address as string
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.LOCAL_MAC_ADDRESS,
-    })
-    public String getAddress() {
-        try {
-            return mManagerService.getAddress(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Get the friendly Bluetooth name of the local Bluetooth adapter.
-     * <p>This name is visible to remote Bluetooth devices.
-     *
-     * @return the Bluetooth name, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getName() {
-        try {
-            return mManagerService.getName(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /** {@hide} */
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public int getNameLengthForAdvertise() {
-        try {
-            return mService.getNameLengthForAdvertise(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return -1;
-    }
-
-    /**
-     * Factory reset bluetooth settings.
-     *
-     * @return true to indicate that the config file was successfully cleared
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean factoryReset() {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null && mService.factoryReset(mAttributionSource)
-                    && mManagerService != null
-                    && mManagerService.onFactoryReset(mAttributionSource)) {
-                return true;
-            }
-            Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
-            BluetoothProperties.factory_reset(true);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Get the UUIDs supported by the local Bluetooth adapter.
-     *
-     * @return the UUIDs supported by the local Bluetooth Adapter.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @Nullable ParcelUuid[] getUuids() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getUuids(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Set the friendly Bluetooth name of the local Bluetooth adapter.
-     * <p>This name is visible to remote Bluetooth devices.
-     * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8
-     * encoding, although many remote devices can only display the first
-     * 40 characters, and some may be limited to just 20.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @param name a valid Bluetooth name
-     * @return true if the name was set, false otherwise
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setName(String name) {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setName(name, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
-     * adapter.
-     *
-     * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothClass getBluetoothClass() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getBluetoothClass(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
-     * adapter.
-     *
-     * <p>Note: This value persists across system reboot.
-     *
-     * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to.
-     * @return true if successful, false if unsuccessful.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setBluetoothClass(bluetoothClass, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the Input/Output capability of the device for classic Bluetooth.
-     *
-     * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *         {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
-     *         {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @IoCapability
-    public int getIoCapability() {
-        if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.getIoCapability(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-    }
-
-    /**
-     * Sets the Input/Output capability of the device for classic Bluetooth.
-     *
-     * <p>Changing the Input/Output capability of a device only takes effect on restarting the
-     * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
-     * and {@link BluetoothAdapter#enable()} to see the changes.
-     *
-     * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *                   {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
-     *                   {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setIoCapability(@IoCapability int capability) {
-        if (getState() != STATE_ON) return false;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.setIoCapability(capability, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the Input/Output capability of the device for BLE operations.
-     *
-     * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *         {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
-     *         {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @IoCapability
-    public int getLeIoCapability() {
-        if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.getLeIoCapability(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
-    }
-
-    /**
-     * Sets the Input/Output capability of the device for BLE operations.
-     *
-     * <p>Changing the Input/Output capability of a device only takes effect on restarting the
-     * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
-     * and {@link BluetoothAdapter#enable()} to see the changes.
-     *
-     * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
-     *                   {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
-     *                   {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setLeIoCapability(@IoCapability int capability) {
-        if (getState() != STATE_ON) return false;
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.getMessage(), e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Get the current Bluetooth scan mode of the local Bluetooth adapter.
-     * <p>The Bluetooth scan mode determines if the local adapter is
-     * connectable and/or discoverable from remote Bluetooth devices.
-     * <p>Possible values are:
-     * {@link #SCAN_MODE_NONE},
-     * {@link #SCAN_MODE_CONNECTABLE},
-     * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return scan mode
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @ScanMode
-    public int getScanMode() {
-        if (getState() != STATE_ON) {
-            return SCAN_MODE_NONE;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getScanMode(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return SCAN_MODE_NONE;
-    }
-
-    /**
-     * Set the local Bluetooth adapter connectablility and discoverability.
-     * <p>If the scan mode is set to {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
-     * it will change to {@link #SCAN_MODE_CONNECTABLE} after the discoverable timeout.
-     * The discoverable timeout can be set with {@link #setDiscoverableTimeout} and
-     * checked with {@link #getDiscoverableTimeout}. By default, the timeout is usually
-     * 120 seconds on phones which is enough for a remote device to initiate and complete
-     * its discovery process.
-     * <p>Applications cannot set the scan mode. They should use
-     * {@link #ACTION_REQUEST_DISCOVERABLE} instead.
-     *
-     * @param mode represents the desired state of the local device scan mode
-     *
-     * @return status code indicating whether the scan mode was successfully set
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    @ScanModeStatusCode
-    public int setScanMode(@ScanMode int mode) {
-        if (getState() != STATE_ON) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setScanMode(mode, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Get the timeout duration of the {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
-     *
-     * @return the duration of the discoverable timeout or null if an error has occurred
-     */
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public @Nullable Duration getDiscoverableTimeout() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                long timeout = mService.getDiscoverableTimeout(mAttributionSource);
-                return (timeout == -1) ? null : Duration.ofSeconds(timeout);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Set the total time the Bluetooth local adapter will stay discoverable when
-     * {@link #setScanMode} is called with {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} mode.
-     * After this timeout, the scan mode will fallback to {@link #SCAN_MODE_CONNECTABLE}.
-     * <p>If <code>timeout</code> is set to 0, no timeout will occur and the scan mode will
-     * be persisted until a subsequent call to {@link #setScanMode}.
-     *
-     * @param timeout represents the total duration the local Bluetooth adapter will remain
-     *                discoverable, or no timeout if set to 0
-     * @return whether the timeout was successfully set
-     * @throws IllegalArgumentException if <code>timeout</code> duration in seconds is more
-     *         than {@link Integer#MAX_VALUE}
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    @ScanModeStatusCode
-    public int setDiscoverableTimeout(@NonNull Duration timeout) {
-        if (getState() != STATE_ON) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        if (timeout.toSeconds() > Integer.MAX_VALUE) {
-            throw new IllegalArgumentException("Timeout in seconds must be less or equal to "
-                    + Integer.MAX_VALUE);
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.setDiscoverableTimeout(timeout.toSeconds(), mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Get the end time of the latest remote device discovery process.
-     *
-     * @return the latest time that the bluetooth adapter was/will be in discovery mode, in
-     * milliseconds since the epoch. This time can be in the future if {@link #startDiscovery()} has
-     * been called recently.
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public long getDiscoveryEndMillis() {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getDiscoveryEndMillis(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return -1;
-    }
-
-    /**
-     * Start the remote device discovery process.
-     * <p>The discovery process usually involves an inquiry scan of about 12
-     * seconds, followed by a page scan of each new device to retrieve its
-     * Bluetooth name.
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_DISCOVERY_STARTED} and {@link
-     * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
-     * discovery starts and completes. Register for {@link
-     * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
-     * are found.
-     * <p>Device discovery is a heavyweight procedure. New connections to
-     * remote Bluetooth devices should not be attempted while discovery is in
-     * progress, and existing connections will experience limited bandwidth
-     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
-     * discovery. Discovery is not managed by the Activity,
-     * but is run as a system service, so an application should always call
-     * {@link BluetoothAdapter#cancelDiscovery()} even if it
-     * did not directly request a discovery, just to be sure.
-     * <p>Device discovery will only find remote devices that are currently
-     * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
-     * not discoverable by default, and need to be entered into a special mode.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED}
-     * with {@link #STATE_ON} to get the updated value.
-     * <p>If a device is currently bonding, this request will be queued and executed once that
-     * device has finished bonding. If a request is already queued, this request will be ignored.
-     *
-     * @return true on success, false on error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean startDiscovery() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.startDiscovery(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Cancel the current device discovery process.
-     * <p>Because discovery is a heavyweight procedure for the Bluetooth
-     * adapter, this method should always be called before attempting to connect
-     * to a remote device with {@link
-     * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by
-     * the  Activity, but is run as a system service, so an application should
-     * always call cancel discovery even if it did not directly request a
-     * discovery, just to be sure.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return true on success, false on error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean cancelDiscovery() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.cancelDiscovery(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if the local Bluetooth adapter is currently in the device
-     * discovery process.
-     * <p>Device discovery is a heavyweight procedure. New connections to
-     * remote Bluetooth devices should not be attempted while discovery is in
-     * progress, and existing connections will experience limited bandwidth
-     * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
-     * discovery.
-     * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
-     * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
-     * starts or completes.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return false. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return true if discovering
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean isDiscovering() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isDiscovering(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Removes the active device for the grouping of @ActiveDeviceUse specified
-     *
-     * @param profiles represents the purpose for which we are setting this as the active device.
-     *                 Possible values are:
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
-     * @return false on immediate error, true otherwise
-     * @throws IllegalArgumentException if device is null or profiles is not one of
-     * {@link ActiveDeviceUse}
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
-        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
-                && profiles != ACTIVE_DEVICE_ALL) {
-            Log.e(TAG, "Invalid profiles param value in removeActiveDevice");
-            throw new IllegalArgumentException("Profiles must be one of "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
-                return mService.removeActiveDevice(profiles, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * Sets device as the active devices for the profiles passed into the function
-     *
-     * @param device is the remote bluetooth device
-     * @param profiles represents the purpose for which we are setting this as the active device.
-     *                 Possible values are:
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
-     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
-     * @return false on immediate error, true otherwise
-     * @throws IllegalArgumentException if device is null or profiles is not one of
-     * {@link ActiveDeviceUse}
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean setActiveDevice(@NonNull BluetoothDevice device,
-            @ActiveDeviceUse int profiles) {
-        if (device == null) {
-            Log.e(TAG, "setActiveDevice: Null device passed as parameter");
-            throw new IllegalArgumentException("device cannot be null");
-        }
-        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
-                && profiles != ACTIVE_DEVICE_ALL) {
-            Log.e(TAG, "Invalid profiles param value in setActiveDevice");
-            throw new IllegalArgumentException("Profiles must be one of "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
-                    + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                if (DBG) {
-                    Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
-                }
-                return mService.setActiveDevice(device, profiles, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * Get the active devices for the BluetoothProfile specified
-     *
-     * @param profile is the profile from which we want the active devices.
-     *                Possible values are:
-     *                {@link BluetoothProfile#HEADSET},
-     *                {@link BluetoothProfile#A2DP},
-     *                {@link BluetoothProfile#HEARING_AID}
-     *                {@link BluetoothProfile#LE_AUDIO}
-     * @return A list of active bluetooth devices
-     * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile}
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) {
-        if (profile != BluetoothProfile.HEADSET
-                && profile != BluetoothProfile.A2DP
-                && profile != BluetoothProfile.HEARING_AID
-                && profile != BluetoothProfile.LE_AUDIO) {
-            Log.e(TAG, "Invalid profile param value in getActiveDevices");
-            throw new IllegalArgumentException("Profiles must be one of "
-                    + "BluetoothProfile.A2DP, "
-                    + "BluetoothProfile.HEARING_AID, or"
-                    + "BluetoothProfile.HEARING_AID"
-                    + "BluetoothProfile.LE_AUDIO");
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                if (DBG) {
-                    Log.d(TAG, "getActiveDevices(profile= "
-                            + BluetoothProfile.getProfileName(profile) + ")");
-                }
-                return mService.getActiveDevices(profile, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return new ArrayList<>();
-    }
-
-    /**
-     * Return true if the multi advertisement is supported by the chipset
-     *
-     * @return true if Multiple Advertisement feature is supported
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isMultipleAdvertisementSupported() {
-        if (getState() != STATE_ON) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isMultiAdvertisementSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p>
-     *
-     * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and
-     * fetch scan results even when Bluetooth is turned off.<p>
-     *
-     * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresNoPermission
-    public boolean isBleScanAlwaysAvailable() {
-        try {
-            return mManagerService.isBleScanAlwaysAvailable();
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception when calling isBleScanAlwaysAvailable", e);
-            return false;
-        }
-    }
-
-    private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
-            "cache_key.bluetooth.is_offloaded_filtering_supported";
-    private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
-            new PropertyInvalidatedCache<Void, Boolean>(
-                8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public Boolean recompute(Void query) {
-                    try {
-                        mServiceLock.readLock().lock();
-                        if (mService != null) {
-                            return mService.isOffloadedFilteringSupported();
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
-                    } finally {
-                        mServiceLock.readLock().unlock();
-                    }
-                    return false;
-
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableIsOffloadedFilteringSupportedCache() {
-        mBluetoothFilteringCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateIsOffloadedFilteringSupportedCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
-    }
-
-    /**
-     * Return true if offloaded filters are supported
-     *
-     * @return true if chipset supports on-chip filtering
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isOffloadedFilteringSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        return mBluetoothFilteringCache.query(null);
-    }
-
-    /**
-     * Return true if offloaded scan batching is supported
-     *
-     * @return true if chipset supports on-chip scan batching
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isOffloadedScanBatchingSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isOffloadedScanBatchingSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE 2M PHY feature is supported.
-     *
-     * @return true if chipset supports LE 2M PHY feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLe2MPhySupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLe2MPhySupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE Coded PHY feature is supported.
-     *
-     * @return true if chipset supports LE Coded PHY feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLeCodedPhySupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLeCodedPhySupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE Extended Advertising feature is supported.
-     *
-     * @return true if chipset supports LE Extended Advertising feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLeExtendedAdvertisingSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLeExtendedAdvertisingSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
-     * Return true if LE Periodic Advertising feature is supported.
-     *
-     * @return true if chipset supports LE Periodic Advertising feature
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public boolean isLePeriodicAdvertisingSupported() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLePeriodicAdvertisingSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.FEATURE_SUPPORTED,
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.FEATURE_NOT_SUPPORTED,
-    })
-    public @interface LeFeatureReturnValues {}
-
-    /**
-     * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is
-     * supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not
-     * supported, or an error code.
-     *
-     * @return whether the LE audio is supported
-     */
-    @RequiresNoPermission
-    public @LeFeatureReturnValues int isLeAudioSupported() {
-        if (!getLeAccess()) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLeAudioSupported();
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if LE Periodic Advertising Sync
-     * Transfer Sender feature is supported,
-     * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not supported, or
-     * an error code
-     *
-     * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature
-     */
-    @RequiresNoPermission
-    public @LeFeatureReturnValues int isLePeriodicAdvertisingSyncTransferSenderSupported() {
-        if (!getLeAccess()) {
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isLePeriodicAdvertisingSyncTransferSenderSupported();
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Return the maximum LE advertising data length in bytes,
-     * if LE Extended Advertising feature is supported, 0 otherwise.
-     *
-     * @return the maximum LE advertising data length.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public int getLeMaximumAdvertisingDataLength() {
-        if (!getLeAccess()) {
-            return 0;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getLeMaximumAdvertisingDataLength();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return 0;
-    }
-
-    /**
-     * Return true if Hearing Aid Profile is supported.
-     *
-     * @return true if phone supports Hearing Aid Profile
-     */
-    @RequiresNoPermission
-    private boolean isHearingAidProfileSupported() {
-        try {
-            return mManagerService.isHearingAidProfileSupported();
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception when calling isHearingAidProfileSupported", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get the maximum number of connected audio devices.
-     *
-     * @return the maximum number of connected audio devices
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getMaxConnectedAudioDevices() {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getMaxConnectedAudioDevices(mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return 1;
-    }
-
-    /**
-     * Return true if hardware has entries available for matching beacons
-     *
-     * @return true if there are hw entries available for matching beacons
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isHardwareTrackingFiltersAvailable() {
-        if (!getLeAccess()) {
-            return false;
-        }
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported
-                return false;
-            }
-            return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Request the record of {@link BluetoothActivityEnergyInfo} object that
-     * has the activity and energy info. This can be used to ascertain what
-     * the controller has been up to, since the last sample.
-     *
-     * A null value for the activity info object may be sent if the bluetooth service is
-     * unreachable or the device does not support reporting such information.
-     *
-     * @param result The callback to which to send the activity info.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void requestControllerActivityEnergyInfo(ResultReceiver result) {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                mService.requestActivityInfo(result, mAttributionSource);
-                result = null;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
-        } finally {
-            mServiceLock.readLock().unlock();
-            if (result != null) {
-                // Only send an immediate result if we failed.
-                result.send(0, null);
-            }
-        }
-    }
-
-    /**
-     * Fetches a list of the most recently connected bluetooth devices ordered by how recently they
-     * were connected with most recently first and least recently last
-     *
-     * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were
-     * connected
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
-        if (getState() != STATE_ON) {
-            return new ArrayList<>();
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return Attributable.setAttributionSource(
-                        mService.getMostRecentlyConnectedDevices(mAttributionSource),
-                        mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return new ArrayList<>();
-    }
-
-    /**
-     * Return the set of {@link BluetoothDevice} objects that are bonded
-     * (paired) to the local adapter.
-     * <p>If Bluetooth state is not {@link #STATE_ON}, this API
-     * will return an empty set. After turning on Bluetooth,
-     * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
-     * to get the updated value.
-     *
-     * @return unmodifiable set of {@link BluetoothDevice}, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public Set<BluetoothDevice> getBondedDevices() {
-        if (getState() != STATE_ON) {
-            return toDeviceSet(Arrays.asList());
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return toDeviceSet(Attributable.setAttributionSource(
-                        Arrays.asList(mService.getBondedDevices(mAttributionSource)),
-                        mAttributionSource));
-            }
-            return toDeviceSet(Arrays.asList());
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return null;
-    }
-
-    /**
-     * Gets the currently supported profiles by the adapter.
-     *
-     * <p> This can be used to check whether a profile is supported before attempting
-     * to connect to its respective proxy.
-     *
-     * @return a list of integers indicating the ids of supported profiles as defined in {@link
-     * BluetoothProfile}.
-     * @hide
-     */
-    @RequiresNoPermission
-    public @NonNull List<Integer> getSupportedProfiles() {
-        final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
-
-        try {
-            synchronized (mManagerCallback) {
-                if (mService != null) {
-                    final long supportedProfilesBitMask = mService.getSupportedProfiles();
-
-                    for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
-                        if ((supportedProfilesBitMask & (1 << i)) != 0) {
-                            supportedProfiles.add(i);
-                        }
-                    }
-                } else {
-                    // Bluetooth is disabled. Just fill in known supported Profiles
-                    if (isHearingAidProfileSupported()) {
-                        supportedProfiles.add(BluetoothProfile.HEARING_AID);
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "getSupportedProfiles:", e);
-        }
-        return supportedProfiles;
-    }
-
-    private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY =
-            "cache_key.bluetooth.get_adapter_connection_state";
-    private final PropertyInvalidatedCache<Void, Integer>
-            mBluetoothGetAdapterConnectionStateCache =
-            new PropertyInvalidatedCache<Void, Integer> (
-                8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) {
-                /**
-                 * This method must not be called when mService is null.
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public Integer recompute(Void query) {
-                    try {
-                        return mService.getAdapterConnectionState();
-                    } catch (RemoteException e) {
-                        throw e.rethrowAsRuntimeException();
-                    }
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableGetAdapterConnectionStateCache() {
-        mBluetoothGetAdapterConnectionStateCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateGetAdapterConnectionStateCache() {
-        PropertyInvalidatedCache.invalidateCache(
-            BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY);
-    }
-
-    /**
-     * Get the current connection state of the local Bluetooth adapter.
-     * This can be used to check whether the local Bluetooth adapter is connected
-     * to any profile of any other remote Bluetooth Device.
-     *
-     * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED}
-     * intent to get the connection state of the adapter.
-     *
-     * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, {@link
-     * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public int getConnectionState() {
-        if (getState() != STATE_ON) {
-            return BluetoothAdapter.STATE_DISCONNECTED;
-        }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mBluetoothGetAdapterConnectionStateCache.query(null);
-            }
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "getConnectionState:", e.getCause());
-            } else {
-                throw e;
-            }
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothAdapter.STATE_DISCONNECTED;
-    }
-
-    private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
-            "cache_key.bluetooth.get_profile_connection_state";
-    private final PropertyInvalidatedCache<Integer, Integer>
-            mGetProfileConnectionStateCache =
-            new PropertyInvalidatedCache<Integer, Integer>(
-                8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public Integer recompute(Integer query) {
-                    try {
-                        mServiceLock.readLock().lock();
-                        if (mService != null) {
-                            return mService.getProfileConnectionState(query);
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "getProfileConnectionState:", e);
-                    } finally {
-                        mServiceLock.readLock().unlock();
-                    }
-                    return BluetoothProfile.STATE_DISCONNECTED;
-                }
-                @Override
-                public String queryToString(Integer query) {
-                    return String.format("getProfileConnectionState(profile=\"%d\")",
-                                         query);
-                }
-            };
-
-    /** @hide */
-    @RequiresNoPermission
-    public void disableGetProfileConnectionStateCache() {
-        mGetProfileConnectionStateCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateGetProfileConnectionStateCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
-    }
-
-    /**
-     * Get the current connection state of a profile.
-     * This function can be used to check whether the local Bluetooth adapter
-     * is connected to any remote device for a specific profile.
-     * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
-     *
-     * <p> Return value can be one of
-     * {@link BluetoothProfile#STATE_DISCONNECTED},
-     * {@link BluetoothProfile#STATE_CONNECTING},
-     * {@link BluetoothProfile#STATE_CONNECTED},
-     * {@link BluetoothProfile#STATE_DISCONNECTING}
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public int getProfileConnectionState(int profile) {
-        if (getState() != STATE_ON) {
-            return BluetoothProfile.STATE_DISCONNECTED;
-        }
-        return mGetProfileConnectionStateCache.query(new Integer(profile));
-    }
-
-    /**
-     * Create a listening, secure RFCOMM Bluetooth socket.
-     * <p>A remote device connecting to this socket will be authenticated and
-     * communication on this socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>Valid RFCOMM channels are in range 1 to 30.
-     *
-     * @param channel RFCOMM channel to listen on
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
-        return listenUsingRfcommOn(channel, false, false);
-    }
-
-    /**
-     * Create a listening, secure RFCOMM Bluetooth socket.
-     * <p>A remote device connecting to this socket will be authenticated and
-     * communication on this socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>Valid RFCOMM channels are in range 1 to 30.
-     * <p>To auto assign a channel without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
-     *
-     * @param channel RFCOMM channel to listen on
-     * @param mitm enforce person-in-the-middle protection for authentication.
-     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
-     * connections.
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
-            boolean min16DigitPin) throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm,
-                        min16DigitPin);
-        int errno = socket.mSocket.bindListen();
-        if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
-     * <p>A remote device connecting to this socket will be authenticated and
-     * communication on this socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>The system will assign an unused RFCOMM channel to listen on.
-     * <p>The system will also register a Service Discovery
-     * Protocol (SDP) record with the local SDP server containing the specified
-     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
-     * can use the same UUID to query our SDP server and discover which channel
-     * to connect to. This SDP record will be removed when this socket is
-     * closed, or if this application closes unexpectedly.
-     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
-     * connect to this socket from another device using the same {@link UUID}.
-     *
-     * @param name service name for SDP record
-     * @param uuid uuid for SDP record
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
-            throws IOException {
-        return createNewRfcommSocketAndRecord(name, uuid, true, true);
-    }
-
-    /**
-     * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
-     * <p>The link key is not required to be authenticated, i.e the communication may be
-     * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices,
-     * the link will be encrypted, as encryption is mandatory.
-     * For legacy devices (pre Bluetooth 2.1 devices) the link will not
-     * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
-     * encrypted and authenticated communication channel is desired.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>The system will assign an unused RFCOMM channel to listen on.
-     * <p>The system will also register a Service Discovery
-     * Protocol (SDP) record with the local SDP server containing the specified
-     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
-     * can use the same UUID to query our SDP server and discover which channel
-     * to connect to. This SDP record will be removed when this socket is
-     * closed, or if this application closes unexpectedly.
-     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
-     * connect to this socket from another device using the same {@link UUID}.
-     *
-     * @param name service name for SDP record
-     * @param uuid uuid for SDP record
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
-            throws IOException {
-        return createNewRfcommSocketAndRecord(name, uuid, false, false);
-    }
-
-    /**
-     * Create a listening, encrypted,
-     * RFCOMM Bluetooth socket with Service Record.
-     * <p>The link will be encrypted, but the link key is not required to be authenticated
-     * i.e the communication is vulnerable to Person In the Middle attacks. Use
-     * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
-     * <p> Use this socket if authentication of link key is not possible.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not have
-     * an input and output capability or just has the ability to display a numeric key,
-     * a secure socket connection is not possible and this socket can be used.
-     * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
-     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
-     * connections from a listening {@link BluetoothServerSocket}.
-     * <p>The system will assign an unused RFCOMM channel to listen on.
-     * <p>The system will also register a Service Discovery
-     * Protocol (SDP) record with the local SDP server containing the specified
-     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
-     * can use the same UUID to query our SDP server and discover which channel
-     * to connect to. This SDP record will be removed when this socket is
-     * closed, or if this application closes unexpectedly.
-     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
-     * connect to this socket from another device using the same {@link UUID}.
-     *
-     * @param name service name for SDP record
-     * @param uuid uuid for SDP record
-     * @return a listening RFCOMM BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or channel in use.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
-            throws IOException {
-        return createNewRfcommSocketAndRecord(name, uuid, false, true);
-    }
-
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
-            boolean auth, boolean encrypt) throws IOException {
-        BluetoothServerSocket socket;
-        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt,
-                new ParcelUuid(uuid));
-        socket.setServiceName(name);
-        int errno = socket.mSocket.bindListen();
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct an unencrypted, unauthenticated, RFCOMM server socket.
-     * Call #accept to retrieve connections to this socket.
-     *
-     * @return An RFCOMM BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct an encrypted, authenticated, L2CAP server socket.
-     * Call #accept to retrieve connections to this socket.
-     * <p>To auto assign a port without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
-     *
-     * @param port the PSM to listen on
-     * @param mitm enforce person-in-the-middle protection for authentication.
-     * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
-     * connections.
-     * @return An L2CAP BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
-            throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm,
-                        min16DigitPin);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            int assignedChannel = socket.mSocket.getPort();
-            if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
-            socket.setChannel(assignedChannel);
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct an encrypted, authenticated, L2CAP server socket.
-     * Call #accept to retrieve connections to this socket.
-     * <p>To auto assign a port without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
-     *
-     * @param port the PSM to listen on
-     * @return An L2CAP BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
-        return listenUsingL2capOn(port, false, false);
-    }
-
-    /**
-     * Construct an insecure L2CAP server socket.
-     * Call #accept to retrieve connections to this socket.
-     * <p>To auto assign a port without creating a SDP record use
-     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
-     *
-     * @param port the PSM to listen on
-     * @return An L2CAP BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
-        Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
-                                          false);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            int assignedChannel = socket.mSocket.getPort();
-            if (DBG) {
-                Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
-                        + assignedChannel);
-            }
-            socket.setChannel(assignedChannel);
-        }
-        if (errno != 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-
-    }
-
-    /**
-     * Read the local Out of Band Pairing Data
-     *
-     * @return Pair<byte[], byte[]> of Hash and Randomizer
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public Pair<byte[], byte[]> readOutOfBandData() {
-        return null;
-    }
-
-    /**
-     * Get the profile proxy object associated with the profile.
-     *
-     * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link
-     * BluetoothProfile#GATT_SERVER}. Clients must implement {@link
-     * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the
-     * proxy object.
-     *
-     * @param context Context of the application
-     * @param listener The service Listener for connection callbacks.
-     * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
-     * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link
-     * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}.
-     * @return true on success, false on error
-     */
-    @SuppressLint({
-        "AndroidFrameworkRequiresPermission",
-        "AndroidFrameworkBluetoothPermission"
-    })
-    public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
-            int profile) {
-        if (context == null || listener == null) {
-            return false;
-        }
-
-        if (profile == BluetoothProfile.HEADSET) {
-            BluetoothHeadset headset = new BluetoothHeadset(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.A2DP) {
-            BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.A2DP_SINK) {
-            BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
-            BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HID_HOST) {
-            BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.PAN) {
-            BluetoothPan pan = new BluetoothPan(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.PBAP) {
-            BluetoothPbap pbap = new BluetoothPbap(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HEALTH) {
-            Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
-            return false;
-        } else if (profile == BluetoothProfile.MAP) {
-            BluetoothMap map = new BluetoothMap(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
-            BluetoothHeadsetClient headsetClient =
-                    new BluetoothHeadsetClient(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.SAP) {
-            BluetoothSap sap = new BluetoothSap(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.PBAP_CLIENT) {
-            BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.MAP_CLIENT) {
-            BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HID_DEVICE) {
-            BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.HEARING_AID) {
-            if (isHearingAidProfileSupported()) {
-                BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this);
-                return true;
-            }
-            return false;
-        } else if (profile == BluetoothProfile.LE_AUDIO) {
-            BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.VOLUME_CONTROL) {
-            BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) {
-            BluetoothCsipSetCoordinator csipSetCoordinator =
-                    new BluetoothCsipSetCoordinator(context, listener, this);
-            return true;
-        } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
-            BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Close the connection of the profile proxy to the Service.
-     *
-     * <p> Clients should call this when they are no longer using
-     * the proxy obtained from {@link #getProfileProxy}.
-     * Profile can be one of  {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}
-     *
-     * @param profile
-     * @param proxy Profile proxy object
-     */
-    @SuppressLint({
-            "AndroidFrameworkRequiresPermission",
-            "AndroidFrameworkBluetoothPermission"
-    })
-    public void closeProfileProxy(int profile, BluetoothProfile proxy) {
-        if (proxy == null) {
-            return;
-        }
-
-        switch (profile) {
-            case BluetoothProfile.HEADSET:
-                BluetoothHeadset headset = (BluetoothHeadset) proxy;
-                headset.close();
-                break;
-            case BluetoothProfile.A2DP:
-                BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
-                a2dp.close();
-                break;
-            case BluetoothProfile.A2DP_SINK:
-                BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink) proxy;
-                a2dpSink.close();
-                break;
-            case BluetoothProfile.AVRCP_CONTROLLER:
-                BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy;
-                avrcp.close();
-                break;
-            case BluetoothProfile.HID_HOST:
-                BluetoothHidHost iDev = (BluetoothHidHost) proxy;
-                iDev.close();
-                break;
-            case BluetoothProfile.PAN:
-                BluetoothPan pan = (BluetoothPan) proxy;
-                pan.close();
-                break;
-            case BluetoothProfile.PBAP:
-                BluetoothPbap pbap = (BluetoothPbap) proxy;
-                pbap.close();
-                break;
-            case BluetoothProfile.GATT:
-                BluetoothGatt gatt = (BluetoothGatt) proxy;
-                gatt.close();
-                break;
-            case BluetoothProfile.GATT_SERVER:
-                BluetoothGattServer gattServer = (BluetoothGattServer) proxy;
-                gattServer.close();
-                break;
-            case BluetoothProfile.MAP:
-                BluetoothMap map = (BluetoothMap) proxy;
-                map.close();
-                break;
-            case BluetoothProfile.HEADSET_CLIENT:
-                BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient) proxy;
-                headsetClient.close();
-                break;
-            case BluetoothProfile.SAP:
-                BluetoothSap sap = (BluetoothSap) proxy;
-                sap.close();
-                break;
-            case BluetoothProfile.PBAP_CLIENT:
-                BluetoothPbapClient pbapClient = (BluetoothPbapClient) proxy;
-                pbapClient.close();
-                break;
-            case BluetoothProfile.MAP_CLIENT:
-                BluetoothMapClient mapClient = (BluetoothMapClient) proxy;
-                mapClient.close();
-                break;
-            case BluetoothProfile.HID_DEVICE:
-                BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
-                hidDevice.close();
-                break;
-            case BluetoothProfile.HEARING_AID:
-                BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
-                hearingAid.close();
-                break;
-            case BluetoothProfile.LE_AUDIO:
-                BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
-                leAudio.close();
-                break;
-            case BluetoothProfile.VOLUME_CONTROL:
-                BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
-                vcs.close();
-                break;
-            case BluetoothProfile.CSIP_SET_COORDINATOR:
-                BluetoothCsipSetCoordinator csipSetCoordinator =
-                        (BluetoothCsipSetCoordinator) proxy;
-                csipSetCoordinator.close();
-                break;
-            case BluetoothProfile.LE_CALL_CONTROL:
-                BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
-                tbs.close();
-                break;
-        }
-    }
-
-    private static final IBluetoothManagerCallback sManagerCallback =
-            new IBluetoothManagerCallback.Stub() {
-                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
-                    }
-
-                    synchronized (sServiceLock) {
-                        sService = bluetoothService;
-                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceUp(bluetoothService);
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceUp: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
-                    }
-                }
-
-                public void onBluetoothServiceDown() {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothServiceDown");
-                    }
-
-                    synchronized (sServiceLock) {
-                        sService = null;
-                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceDown();
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
-                    }
-                }
-
-                public void onBrEdrDown() {
-                    if (VDBG) {
-                        Log.i(TAG, "onBrEdrDown");
-                    }
-
-                    synchronized (sServiceLock) {
-                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBrEdrDown();
-                                } else {
-                                    Log.d(TAG, "onBrEdrDown: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
-                    }
-                }
-            };
-
-    private final IBluetoothManagerCallback mManagerCallback =
-            new IBluetoothManagerCallback.Stub() {
-                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    synchronized (mServiceLock.writeLock()) {
-                        mService = bluetoothService;
-                    }
-                    synchronized (mMetadataListeners) {
-                        mMetadataListeners.forEach((device, pair) -> {
-                            try {
-                                mService.registerMetadataListener(mBluetoothMetadataListener,
-                                        device, mAttributionSource);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Failed to register metadata listener", e);
-                            }
-                        });
-                    }
-                    synchronized (mBluetoothConnectionCallbackExecutorMap) {
-                        if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-                            try {
-                                mService.registerBluetoothConnectionCallback(mConnectionCallback,
-                                        mAttributionSource);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
-                                        + "connection callback", e);
-                            }
-                        }
-                    }
-                }
-
-                public void onBluetoothServiceDown() {
-                    synchronized (mServiceLock.writeLock()) {
-                        mService = null;
-                        if (mLeScanClients != null) {
-                            mLeScanClients.clear();
-                        }
-                        if (mBluetoothLeAdvertiser != null) {
-                            mBluetoothLeAdvertiser.cleanup();
-                        }
-                        if (mBluetoothLeScanner != null) {
-                            mBluetoothLeScanner.cleanup();
-                        }
-                    }
-                }
-
-                public void onBrEdrDown() {
-                }
-            };
-
-    /**
-     * Enable the Bluetooth Adapter, but don't auto-connect devices
-     * and don't persist state. Only for use by system applications.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enableNoAutoConnect() {
-        if (isEnabled()) {
-            if (DBG) {
-                Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
-            }
-            return true;
-        }
-        try {
-            return mManagerService.enableNoAutoConnect(mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST,
-    })
-    public @interface OobError {}
-
-    /**
-     * Provides callback methods for receiving {@link OobData} from the host stack, as well as an
-     * error interface in order to allow the caller to determine next steps based on the {@code
-     * ErrorCode}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface OobDataCallback {
-        /**
-         * Handles the {@link OobData} received from the host stack.
-         *
-         * @param transport - whether the {@link OobData} is generated for LE or Classic.
-         * @param oobData - data generated in the host stack(LE) or controller (Classic)
-         */
-        void onOobData(@Transport int transport, @NonNull OobData oobData);
-
-        /**
-         * Provides feedback when things don't go as expected.
-         *
-         * @param errorCode - the code describing the type of error that occurred.
-         */
-        void onError(@OobError int errorCode);
-    }
-
-    /**
-     * Wraps an AIDL interface around an {@link OobDataCallback} interface.
-     *
-     * @see {@link IBluetoothOobDataCallback} for interface definition.
-     *
-     * @hide
-     */
-    public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub {
-        private final OobDataCallback mCallback;
-        private final Executor mExecutor;
-
-        /**
-         * @param callback - object to receive {@link OobData} must be a non null argument
-         *
-         * @throws NullPointerException if the callback is null.
-         */
-        WrappedOobDataCallback(@NonNull OobDataCallback callback,
-                @NonNull @CallbackExecutor Executor executor) {
-            requireNonNull(callback);
-            requireNonNull(executor);
-            mCallback = callback;
-            mExecutor = executor;
-        }
-        /**
-         * Wrapper function to relay to the {@link OobDataCallback#onOobData}
-         *
-         * @param transport - whether the {@link OobData} is generated for LE or Classic.
-         * @param oobData - data generated in the host stack(LE) or controller (Classic)
-         *
-         * @hide
-         */
-        public void onOobData(@Transport int transport, @NonNull OobData oobData) {
-            mExecutor.execute(new Runnable() {
-                public void run() {
-                    mCallback.onOobData(transport, oobData);
-                }
-            });
-        }
-        /**
-         * Wrapper function to relay to the {@link OobDataCallback#onError}
-         *
-         * @param errorCode - the code descibing the type of error that occurred.
-         *
-         * @hide
-         */
-        public void onError(@OobError int errorCode) {
-            mExecutor.execute(new Runnable() {
-                public void run() {
-                    mCallback.onError(errorCode);
-                }
-            });
-        }
-    }
-
-    /**
-     * Fetches a secret data value that can be used for a secure and simple pairing experience.
-     *
-     * <p>This is the Local Out of Band data the comes from the
-     *
-     * <p>This secret is the local Out of Band data.  This data is used to securely and quickly
-     * pair two devices with minimal user interaction.
-     *
-     * <p>For example, this secret can be transferred to a remote device out of band (meaning any
-     * other way besides using bluetooth).  Once the remote device finds this device using the
-     * information given in the data, such as the PUBLIC ADDRESS, the remote device could then
-     * connect to this device using this secret when the pairing sequenece asks for the secret.
-     * This device will respond by automatically accepting the pairing due to the secret being so
-     * trustworthy.
-     *
-     * @param transport - provide type of transport (e.g. LE or Classic).
-     * @param callback - target object to receive the {@link OobData} value.
-     *
-     * @throws NullPointerException if callback is null.
-     * @throws IllegalArgumentException if the transport is not valid.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void generateLocalOobData(@Transport int transport,
-            @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
-        if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
-                != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Invalid transport '" + transport + "'!");
-        }
-        requireNonNull(callback);
-        if (!isEnabled()) {
-            Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
-            callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED);
-        } else {
-            try {
-                mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
-                        executor), mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-    }
-
-    /**
-     * Enable control of the Bluetooth Adapter for a single application.
-     *
-     * <p>Some applications need to use Bluetooth for short periods of time to
-     * transfer data but don't want all the associated implications like
-     * automatic connection to headsets etc.
-     *
-     * <p> Multiple applications can call this. This is reference counted and
-     * Bluetooth disabled only when no one else is using it. There will be no UI
-     * shown to the user while bluetooth is being enabled. Any user action will
-     * override this call. For example, if user wants Bluetooth on and the last
-     * user of this API wanted to disable Bluetooth, Bluetooth will not be
-     * turned off.
-     *
-     * <p> This API is only meant to be used by internal applications. Third
-     * party applications but use {@link #enable} and {@link #disable} APIs.
-     *
-     * <p> If this API returns true, it means the callback will be called.
-     * The callback will be called with the current state of Bluetooth.
-     * If the state is not what was requested, an internal error would be the
-     * reason. If Bluetooth is already on and if this function is called to turn
-     * it on, the api will return true and a callback will be called.
-     *
-     * @param on True for on, false for off.
-     * @param callback The callback to notify changes to the state.
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean changeApplicationBluetoothState(boolean on,
-            BluetoothStateChangeCallback callback) {
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    public interface BluetoothStateChangeCallback {
-        /**
-         * @hide
-         */
-        void onBluetoothStateChange(boolean on);
-    }
-
-    /**
-     * @hide
-     */
-    public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
-        private BluetoothStateChangeCallback mCallback;
-
-        StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onBluetoothStateChange(boolean on) {
-            mCallback.onBluetoothStateChange(on);
-        }
-    }
-
-    private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) {
-        Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices);
-        return Collections.unmodifiableSet(deviceSet);
-    }
-
-    protected void finalize() throws Throwable {
-        try {
-            removeServiceStateCallback(mManagerCallback);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"
-     * <p>Alphabetic characters must be uppercase to be valid.
-     *
-     * @param address Bluetooth address as string
-     * @return true if the address is valid, false otherwise
-     */
-    public static boolean checkBluetoothAddress(String address) {
-        if (address == null || address.length() != ADDRESS_LENGTH) {
-            return false;
-        }
-        for (int i = 0; i < ADDRESS_LENGTH; i++) {
-            char c = address.charAt(i);
-            switch (i % 3) {
-                case 0:
-                case 1:
-                    if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
-                        // hex character, OK
-                        break;
-                    }
-                    return false;
-                case 2:
-                    if (c == ':') {
-                        break;  // OK
-                    }
-                    return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00"
-     * is a RANDOM STATIC address.
-     *
-     * RANDOM STATIC: (addr & 0xC0) == 0xC0
-     * RANDOM RESOLVABLE: (addr &  0xC0) == 0x40
-     * RANDOM non-RESOLVABLE: (addr &  0xC0) == 0x00
-     *
-     * @param address Bluetooth address as string
-     * @return true if the 2 Most Significant Bits of the address equals 0xC0.
-     *
-     * @hide
-     */
-    public static boolean isAddressRandomStatic(@NonNull String address) {
-        requireNonNull(address);
-        return checkBluetoothAddress(address)
-                && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0;
-    }
-
-    /** {@hide} */
-    @UnsupportedAppUsage
-    @RequiresNoPermission
-    public IBluetoothManager getBluetoothManager() {
-        return mManagerService;
-    }
-
-    /** {@hide} */
-    @RequiresNoPermission
-    public AttributionSource getAttributionSource() {
-        return mAttributionSource;
-    }
-
-    @GuardedBy("sServiceLock")
-    private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks =
-            new WeakHashMap<>();
-
-    /*package*/ IBluetooth getBluetoothService() {
-        synchronized (sServiceLock) {
-            if (sProxyServiceStateCallbacks.isEmpty()) {
-                throw new IllegalStateException(
-                        "Anonymous service access requires at least one lifecycle in process");
-            }
-            return sService;
-        }
-    }
-
-    @UnsupportedAppUsage
-    /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
-        Objects.requireNonNull(cb);
-        synchronized (sServiceLock) {
-            sProxyServiceStateCallbacks.put(cb, null);
-            registerOrUnregisterAdapterLocked();
-            return sService;
-        }
-    }
-
-    /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) {
-        Objects.requireNonNull(cb);
-        synchronized (sServiceLock) {
-            sProxyServiceStateCallbacks.remove(cb);
-            registerOrUnregisterAdapterLocked();
-        }
-    }
-
-    /**
-     * Handle registering (or unregistering) a single process-wide
-     * {@link IBluetoothManagerCallback} based on the presence of local
-     * {@link #sProxyServiceStateCallbacks} clients.
-     */
-    @GuardedBy("sServiceLock")
-    private void registerOrUnregisterAdapterLocked() {
-        final boolean isRegistered = sServiceRegistered;
-        final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty();
-
-        if (isRegistered != wantRegistered) {
-            if (wantRegistered) {
-                try {
-                    sService = mManagerService.registerAdapter(sManagerCallback);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            } else {
-                try {
-                    mManagerService.unregisterAdapter(sManagerCallback);
-                    sService = null;
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-            sServiceRegistered = wantRegistered;
-        }
-    }
-
-    /**
-     * Callback interface used to deliver LE scan results.
-     *
-     * @see #startLeScan(LeScanCallback)
-     * @see #startLeScan(UUID[], LeScanCallback)
-     */
-    public interface LeScanCallback {
-        /**
-         * Callback reporting an LE device found during a device scan initiated
-         * by the {@link BluetoothAdapter#startLeScan} function.
-         *
-         * @param device Identifies the remote device
-         * @param rssi The RSSI value for the remote device as reported by the Bluetooth hardware. 0
-         * if no RSSI value is available.
-         * @param scanRecord The content of the advertisement record offered by the remote device.
-         */
-        void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
-    }
-
-    /**
-     * Register a callback to receive events whenever the bluetooth stack goes down and back up,
-     * e.g. in the event the bluetooth is turned off/on via settings.
-     *
-     * If the bluetooth stack is currently up, there will not be an initial callback call.
-     * You can use the return value as an indication of this being the case.
-     *
-     * Callbacks will be delivered on a binder thread.
-     *
-     * @return whether bluetooth is already up currently
-     *
-     * @hide
-     */
-    public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
-        return getBluetoothService(callback.mRemote) != null;
-    }
-
-    /**
-     * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
-     *
-     * @hide
-     */
-    public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
-        removeServiceStateCallback(callback.mRemote);
-    }
-
-    /**
-     * A callback for {@link #registerServiceLifecycleCallback}
-     *
-     * @hide
-     */
-    public abstract static class ServiceLifecycleCallback {
-
-        /** Called when the bluetooth stack is up */
-        public abstract void onBluetoothServiceUp();
-
-        /** Called when the bluetooth stack is down */
-        public abstract void onBluetoothServiceDown();
-
-        IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
-            @Override
-            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                ServiceLifecycleCallback.this.onBluetoothServiceUp();
-            }
-
-            @Override
-            public void onBluetoothServiceDown() {
-                ServiceLifecycleCallback.this.onBluetoothServiceDown();
-            }
-
-            @Override
-            public void onBrEdrDown() {}
-        };
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices.
-     *
-     * <p>Results of the scan are reported using the
-     * {@link LeScanCallback#onLeScan} callback.
-     *
-     * @param callback the callback LE scan results are delivered
-     * @return true, if the scan was started successfully
-     * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
-     * instead.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean startLeScan(LeScanCallback callback) {
-        return startLeScan(null, callback);
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices, looking for devices that
-     * advertise given services.
-     *
-     * <p>Devices which advertise all specified services are reported using the
-     * {@link LeScanCallback#onLeScan} callback.
-     *
-     * @param serviceUuids Array of services to look for
-     * @param callback the callback LE scan results are delivered
-     * @return true, if the scan was started successfully
-     * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)}
-     * instead.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
-        if (DBG) {
-            Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
-        }
-        if (callback == null) {
-            if (DBG) {
-                Log.e(TAG, "startLeScan: null callback");
-            }
-            return false;
-        }
-        BluetoothLeScanner scanner = getBluetoothLeScanner();
-        if (scanner == null) {
-            if (DBG) {
-                Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
-            }
-            return false;
-        }
-
-        synchronized (mLeScanClients) {
-            if (mLeScanClients.containsKey(callback)) {
-                if (DBG) {
-                    Log.e(TAG, "LE Scan has already started");
-                }
-                return false;
-            }
-
-            try {
-                IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-                if (iGatt == null) {
-                    // BLE is not supported
-                    return false;
-                }
-
-                @SuppressLint("AndroidFrameworkBluetoothPermission")
-                ScanCallback scanCallback = new ScanCallback() {
-                    @Override
-                    public void onScanResult(int callbackType, ScanResult result) {
-                        if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
-                            // Should not happen.
-                            Log.e(TAG, "LE Scan has already started");
-                            return;
-                        }
-                        ScanRecord scanRecord = result.getScanRecord();
-                        if (scanRecord == null) {
-                            return;
-                        }
-                        if (serviceUuids != null) {
-                            List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
-                            for (UUID uuid : serviceUuids) {
-                                uuids.add(new ParcelUuid(uuid));
-                            }
-                            List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
-                            if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
-                                if (DBG) {
-                                    Log.d(TAG, "uuids does not match");
-                                }
-                                return;
-                            }
-                        }
-                        callback.onLeScan(result.getDevice(), result.getRssi(),
-                                scanRecord.getBytes());
-                    }
-                };
-                ScanSettings settings = new ScanSettings.Builder().setCallbackType(
-                        ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
-                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
-                        .build();
-
-                List<ScanFilter> filters = new ArrayList<ScanFilter>();
-                if (serviceUuids != null && serviceUuids.length > 0) {
-                    // Note scan filter does not support matching an UUID array so we put one
-                    // UUID to hardware and match the whole array in callback.
-                    ScanFilter filter =
-                            new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0]))
-                                    .build();
-                    filters.add(filter);
-                }
-                scanner.startScan(filters, settings, scanCallback);
-
-                mLeScanClients.put(callback, scanCallback);
-                return true;
-
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE device scan.
-     *
-     * @param callback used to identify which scan to stop must be the same handle used to start the
-     * scan
-     * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void stopLeScan(LeScanCallback callback) {
-        if (DBG) {
-            Log.d(TAG, "stopLeScan()");
-        }
-        BluetoothLeScanner scanner = getBluetoothLeScanner();
-        if (scanner == null) {
-            return;
-        }
-        synchronized (mLeScanClients) {
-            ScanCallback scanCallback = mLeScanClients.remove(callback);
-            if (scanCallback == null) {
-                if (DBG) {
-                    Log.d(TAG, "scan not started yet");
-                }
-                return;
-            }
-            scanner.stopScan(scanCallback);
-        }
-    }
-
-    /**
-     * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
-     * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
-     * for incoming connections. The supported Bluetooth transport is LE only.
-     * <p>A remote device connecting to this socket will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
-     * {@link BluetoothServerSocket}.
-     * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link
-     * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
-     * closed, Bluetooth is turned off, or the application exits unexpectedly.
-     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
-     * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
-     * socket from another Android device that is given the PSM value.
-     *
-     * @return an L2CAP CoC BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or unable to start this CoC
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull BluetoothServerSocket listenUsingL2capChannel()
-            throws IOException {
-        BluetoothServerSocket socket =
-                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
-                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
-        int errno = socket.mSocket.bindListen();
-        if (errno != 0) {
-            throw new IOException("Error: " + errno);
-        }
-
-        int assignedPsm = socket.mSocket.getPort();
-        if (assignedPsm == 0) {
-            throw new IOException("Error: Unable to assign PSM value");
-        }
-        if (DBG) {
-            Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
-                    + assignedPsm);
-        }
-        socket.setChannel(assignedPsm);
-
-        return socket;
-    }
-
-    /**
-     * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
-     * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
-     * supported Bluetooth transport is LE only.
-     * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
-     * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
-     * authenticated communication channel is desired.
-     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
-     * {@link BluetoothServerSocket}.
-     * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
-     * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released
-     * when this server socket is closed, Bluetooth is turned off, or the application exits
-     * unexpectedly.
-     * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
-     * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
-     * socket from another Android device that is given the PSM value.
-     *
-     * @return an L2CAP CoC BluetoothServerSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions, or unable to start this CoC
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel()
-            throws IOException {
-        BluetoothServerSocket socket =
-                            new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
-                                      SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
-        int errno = socket.mSocket.bindListen();
-        if (errno != 0) {
-            throw new IOException("Error: " + errno);
-        }
-
-        int assignedPsm = socket.mSocket.getPort();
-        if (assignedPsm == 0) {
-            throw new IOException("Error: Unable to assign PSM value");
-        }
-        if (DBG) {
-            Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
-                    + assignedPsm);
-        }
-        socket.setChannel(assignedPsm);
-
-        return socket;
-    }
-
-    /**
-     * Register a {@link #OnMetadataChangedListener} to receive update about metadata
-     * changes for this {@link BluetoothDevice}.
-     * Registration must be done when Bluetooth is ON and will last until
-     * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth
-     * restarted in the middle.
-     * All input parameters should not be null or {@link NullPointerException} will be triggered.
-     * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be
-     * registered once, double registration would cause {@link IllegalArgumentException}.
-     *
-     * @param device {@link BluetoothDevice} that will be registered
-     * @param executor the executor for listener callback
-     * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks
-     * @return true on success, false on error
-     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor}
-     * is null.
-     * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and
-     * {@link BluetoothDevice} are registered twice.
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
-            @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
-        if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");
-
-        final IBluetooth service = mService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener");
-            return false;
-        }
-        if (listener == null) {
-            throw new NullPointerException("listener is null");
-        }
-        if (device == null) {
-            throw new NullPointerException("device is null");
-        }
-        if (executor == null) {
-            throw new NullPointerException("executor is null");
-        }
-
-        synchronized (mMetadataListeners) {
-            List<Pair<OnMetadataChangedListener, Executor>> listenerList =
-                    mMetadataListeners.get(device);
-            if (listenerList == null) {
-                // Create new listener/executor list for registeration
-                listenerList = new ArrayList<>();
-                mMetadataListeners.put(device, listenerList);
-            } else {
-                // Check whether this device was already registed by the lisenter
-                if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) {
-                    throw new IllegalArgumentException("listener was already regestered"
-                            + " for the device");
-                }
-            }
-
-            Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor);
-            listenerList.add(listenerPair);
-
-            boolean ret = false;
-            try {
-                ret = service.registerMetadataListener(mBluetoothMetadataListener, device,
-                        mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, "registerMetadataListener fail", e);
-            } finally {
-                if (!ret) {
-                    // Remove listener registered earlier when fail.
-                    listenerList.remove(listenerPair);
-                    if (listenerList.isEmpty()) {
-                        // Remove the device if its listener list is empty
-                        mMetadataListeners.remove(device);
-                    }
-                }
-            }
-            return ret;
-        }
-    }
-
-    /**
-     * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}.
-     * Unregistration can be done when Bluetooth is either ON or OFF.
-     * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)}
-     * must be called before unregisteration.
-     *
-     * @param device {@link BluetoothDevice} that will be unregistered. It
-     * should not be null or {@link NullPointerException} will be triggered.
-     * @param listener {@link OnMetadataChangedListener} that will be unregistered. It
-     * should not be null or {@link NullPointerException} will be triggered.
-     * @return true on success, false on error
-     * @throws NullPointerException If {@code listener} or {@code device} is null.
-     * @throws IllegalArgumentException If {@code device} has not been registered before.
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
-            @NonNull OnMetadataChangedListener listener) {
-        if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
-        if (device == null) {
-            throw new NullPointerException("device is null");
-        }
-        if (listener == null) {
-            throw new NullPointerException("listener is null");
-        }
-
-        synchronized (mMetadataListeners) {
-            if (!mMetadataListeners.containsKey(device)) {
-                throw new IllegalArgumentException("device was not registered");
-            }
-            // Remove issued listener from the registered device
-            mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener)));
-
-            if (mMetadataListeners.get(device).isEmpty()) {
-                // Unregister to Bluetooth service if all listeners are removed from
-                // the registered device
-                mMetadataListeners.remove(device);
-                final IBluetooth service = mService;
-                if (service == null) {
-                    // Bluetooth is OFF, do nothing to Bluetooth service.
-                    return true;
-                }
-                try {
-                    return service.unregisterMetadataListener(device, mAttributionSource);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "unregisterMetadataListener fail", e);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * This interface is used to implement {@link BluetoothAdapter} metadata listener.
-     * @hide
-     */
-    @SystemApi
-    public interface OnMetadataChangedListener {
-        /**
-         * Callback triggered if the metadata of {@link BluetoothDevice} registered in
-         * {@link #addOnMetadataChangedListener}.
-         *
-         * @param device changed {@link BluetoothDevice}.
-         * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
-         * @param value the new value of metadata as byte array.
-         */
-        void onMetadataChanged(@NonNull BluetoothDevice device, int key,
-                @Nullable byte[] value);
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothConnectionCallback mConnectionCallback =
-            new IBluetoothConnectionCallback.Stub() {
-        @Override
-        public void onDeviceConnected(BluetoothDevice device) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
-                    mBluetoothConnectionCallbackExecutorMap.entrySet()) {
-                BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
-                Executor executor = callbackExecutorEntry.getValue();
-                executor.execute(() -> callback.onDeviceConnected(device));
-            }
-        }
-
-        @Override
-        public void onDeviceDisconnected(BluetoothDevice device, int hciReason) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry:
-                    mBluetoothConnectionCallbackExecutorMap.entrySet()) {
-                BluetoothConnectionCallback callback = callbackExecutorEntry.getKey();
-                Executor executor = callbackExecutorEntry.getValue();
-                executor.execute(() -> callback.onDeviceDisconnected(device, hciReason));
-            }
-        }
-    };
-
-    /**
-     * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device
-     * (classic or low energy) is connected or disconnected.
-     *
-     * @param executor is the callback executor
-     * @param callback is the connection callback you wish to register
-     * @return true if the callback was registered successfully, false otherwise
-     * @throws IllegalArgumentException if the callback is already registered
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull BluetoothConnectionCallback callback) {
-        if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()");
-        if (callback == null) {
-            return false;
-        }
-
-        synchronized (mBluetoothConnectionCallbackExecutorMap) {
-            // If the callback map is empty, we register the service-to-app callback
-            if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-                try {
-                    mServiceLock.readLock().lock();
-                    if (mService != null) {
-                        if (!mService.registerBluetoothConnectionCallback(mConnectionCallback,
-                                mAttributionSource)) {
-                            return false;
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Log.e(TAG, "", e);
-                    mBluetoothConnectionCallbackExecutorMap.remove(callback);
-                } finally {
-                    mServiceLock.readLock().unlock();
-                }
-            }
-
-            // Adds the passed in callback to our map of callbacks to executors
-            if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
-                throw new IllegalArgumentException("This callback has already been registered");
-            }
-            mBluetoothConnectionCallbackExecutorMap.put(callback, executor);
-        }
-
-        return true;
-    }
-
-    /**
-     * Unregisters the BluetoothConnectionCallback that was previously registered by the application
-     *
-     * @param callback is the connection callback you wish to unregister
-     * @return true if the callback was unregistered successfully, false otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean unregisterBluetoothConnectionCallback(
-            @NonNull BluetoothConnectionCallback callback) {
-        if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()");
-        if (callback == null) {
-            return false;
-        }
-
-        synchronized (mBluetoothConnectionCallbackExecutorMap) {
-            if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) {
-                return false;
-            }
-        }
-
-        if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-            return true;
-        }
-
-        // If the callback map is empty, we unregister the service-to-app callback
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.unregisterBluetoothConnectionCallback(mConnectionCallback,
-                        mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth
-     * Low Energy (BLE) device is either connected or disconnected.
-     *
-     * @hide
-     */
-    public abstract static class BluetoothConnectionCallback {
-        /**
-         * Callback triggered when a bluetooth device (classic or BLE) is connected
-         * @param device is the connected bluetooth device
-         */
-        public void onDeviceConnected(BluetoothDevice device) {}
-
-        /**
-         * Callback triggered when a bluetooth device (classic or BLE) is disconnected
-         * @param device is the disconnected bluetooth device
-         * @param reason is the disconnect reason
-         */
-        public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {}
-
-        /**
-         * @hide
-         */
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef(prefix = { "REASON_" }, value = {
-                BluetoothStatusCodes.ERROR_UNKNOWN,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS,
-                BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS})
-        public @interface DisconnectReason {}
-
-        /**
-         * Returns human-readable strings corresponding to {@link DisconnectReason}.
-         */
-        public static String disconnectReasonText(@DisconnectReason int reason) {
-            switch (reason) {
-                case BluetoothStatusCodes.ERROR_UNKNOWN:
-                    return "Reason unknown";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST:
-                    return "Local request";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST:
-                    return "Remote request";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL:
-                    return "Local error";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE:
-                    return "Remote error";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT:
-                    return "Timeout";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY:
-                    return "Security";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY:
-                    return "System policy";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED:
-                    return "Resource constrained";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS:
-                    return "Connection already exists";
-                case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS:
-                    return "Bad parameters";
-                default:
-                    return "Unrecognized disconnect reason: " + reason;
-            }
-        }
-    }
-
-    /**
-     * Converts old constant of priority to the new for connection policy
-     *
-     * @param priority is the priority to convert to connection policy
-     * @return the equivalent connection policy constant to the priority
-     *
-     * @hide
-     */
-    public static @ConnectionPolicy int priorityToConnectionPolicy(int priority) {
-        switch(priority) {
-            case BluetoothProfile.PRIORITY_AUTO_CONNECT:
-                return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-            case BluetoothProfile.PRIORITY_ON:
-                return BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-            case BluetoothProfile.PRIORITY_OFF:
-                return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-            case BluetoothProfile.PRIORITY_UNDEFINED:
-                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
-            default:
-                Log.e(TAG, "setPriority: Invalid priority: " + priority);
-                return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
-        }
-    }
-
-    /**
-     * Converts new constant of connection policy to the old for priority
-     *
-     * @param connectionPolicy is the connection policy to convert to priority
-     * @return the equivalent priority constant to the connectionPolicy
-     *
-     * @hide
-     */
-    public static int connectionPolicyToPriority(@ConnectionPolicy int connectionPolicy) {
-        switch(connectionPolicy) {
-            case BluetoothProfile.CONNECTION_POLICY_ALLOWED:
-                return BluetoothProfile.PRIORITY_ON;
-            case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN:
-                return BluetoothProfile.PRIORITY_OFF;
-            case BluetoothProfile.CONNECTION_POLICY_UNKNOWN:
-                return BluetoothProfile.PRIORITY_UNDEFINED;
-        }
-        return BluetoothProfile.PRIORITY_UNDEFINED;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
deleted file mode 100644
index 41a34e0..0000000
--- a/core/java/android/bluetooth/BluetoothAssignedNumbers.java
+++ /dev/null
@@ -1,1171 +0,0 @@
-/*
- * Copyright (C) 2010 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.bluetooth;
-
-/**
- * Bluetooth Assigned Numbers.
- * <p>
- * For now we only include Company ID values.
- *
- * @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm"> The Official
- * Bluetooth SIG Member Website | Company Identifiers</a>
- */
-public class BluetoothAssignedNumbers {
-
-    // Bluetooth SIG Company ID values
-    /*
-     * Ericsson Technology Licensing.
-     */
-    public static final int ERICSSON_TECHNOLOGY = 0x0000;
-
-    /*
-     * Nokia Mobile Phones.
-     */
-    public static final int NOKIA_MOBILE_PHONES = 0x0001;
-
-    /*
-     * Intel Corp.
-     */
-    public static final int INTEL = 0x0002;
-
-    /*
-     * IBM Corp.
-     */
-    public static final int IBM = 0x0003;
-
-    /*
-     * Toshiba Corp.
-     */
-    public static final int TOSHIBA = 0x0004;
-
-    /*
-     * 3Com.
-     */
-    public static final int THREECOM = 0x0005;
-
-    /*
-     * Microsoft.
-     */
-    public static final int MICROSOFT = 0x0006;
-
-    /*
-     * Lucent.
-     */
-    public static final int LUCENT = 0x0007;
-
-    /*
-     * Motorola.
-     */
-    public static final int MOTOROLA = 0x0008;
-
-    /*
-     * Infineon Technologies AG.
-     */
-    public static final int INFINEON_TECHNOLOGIES = 0x0009;
-
-    /*
-     * Cambridge Silicon Radio.
-     */
-    public static final int CAMBRIDGE_SILICON_RADIO = 0x000A;
-
-    /*
-     * Silicon Wave.
-     */
-    public static final int SILICON_WAVE = 0x000B;
-
-    /*
-     * Digianswer A/S.
-     */
-    public static final int DIGIANSWER = 0x000C;
-
-    /*
-     * Texas Instruments Inc.
-     */
-    public static final int TEXAS_INSTRUMENTS = 0x000D;
-
-    /*
-     * Parthus Technologies Inc.
-     */
-    public static final int PARTHUS_TECHNOLOGIES = 0x000E;
-
-    /*
-     * Broadcom Corporation.
-     */
-    public static final int BROADCOM = 0x000F;
-
-    /*
-     * Mitel Semiconductor.
-     */
-    public static final int MITEL_SEMICONDUCTOR = 0x0010;
-
-    /*
-     * Widcomm, Inc.
-     */
-    public static final int WIDCOMM = 0x0011;
-
-    /*
-     * Zeevo, Inc.
-     */
-    public static final int ZEEVO = 0x0012;
-
-    /*
-     * Atmel Corporation.
-     */
-    public static final int ATMEL = 0x0013;
-
-    /*
-     * Mitsubishi Electric Corporation.
-     */
-    public static final int MITSUBISHI_ELECTRIC = 0x0014;
-
-    /*
-     * RTX Telecom A/S.
-     */
-    public static final int RTX_TELECOM = 0x0015;
-
-    /*
-     * KC Technology Inc.
-     */
-    public static final int KC_TECHNOLOGY = 0x0016;
-
-    /*
-     * Newlogic.
-     */
-    public static final int NEWLOGIC = 0x0017;
-
-    /*
-     * Transilica, Inc.
-     */
-    public static final int TRANSILICA = 0x0018;
-
-    /*
-     * Rohde & Schwarz GmbH & Co. KG.
-     */
-    public static final int ROHDE_AND_SCHWARZ = 0x0019;
-
-    /*
-     * TTPCom Limited.
-     */
-    public static final int TTPCOM = 0x001A;
-
-    /*
-     * Signia Technologies, Inc.
-     */
-    public static final int SIGNIA_TECHNOLOGIES = 0x001B;
-
-    /*
-     * Conexant Systems Inc.
-     */
-    public static final int CONEXANT_SYSTEMS = 0x001C;
-
-    /*
-     * Qualcomm.
-     */
-    public static final int QUALCOMM = 0x001D;
-
-    /*
-     * Inventel.
-     */
-    public static final int INVENTEL = 0x001E;
-
-    /*
-     * AVM Berlin.
-     */
-    public static final int AVM_BERLIN = 0x001F;
-
-    /*
-     * BandSpeed, Inc.
-     */
-    public static final int BANDSPEED = 0x0020;
-
-    /*
-     * Mansella Ltd.
-     */
-    public static final int MANSELLA = 0x0021;
-
-    /*
-     * NEC Corporation.
-     */
-    public static final int NEC = 0x0022;
-
-    /*
-     * WavePlus Technology Co., Ltd.
-     */
-    public static final int WAVEPLUS_TECHNOLOGY = 0x0023;
-
-    /*
-     * Alcatel.
-     */
-    public static final int ALCATEL = 0x0024;
-
-    /*
-     * Philips Semiconductors.
-     */
-    public static final int PHILIPS_SEMICONDUCTORS = 0x0025;
-
-    /*
-     * C Technologies.
-     */
-    public static final int C_TECHNOLOGIES = 0x0026;
-
-    /*
-     * Open Interface.
-     */
-    public static final int OPEN_INTERFACE = 0x0027;
-
-    /*
-     * R F Micro Devices.
-     */
-    public static final int RF_MICRO_DEVICES = 0x0028;
-
-    /*
-     * Hitachi Ltd.
-     */
-    public static final int HITACHI = 0x0029;
-
-    /*
-     * Symbol Technologies, Inc.
-     */
-    public static final int SYMBOL_TECHNOLOGIES = 0x002A;
-
-    /*
-     * Tenovis.
-     */
-    public static final int TENOVIS = 0x002B;
-
-    /*
-     * Macronix International Co. Ltd.
-     */
-    public static final int MACRONIX = 0x002C;
-
-    /*
-     * GCT Semiconductor.
-     */
-    public static final int GCT_SEMICONDUCTOR = 0x002D;
-
-    /*
-     * Norwood Systems.
-     */
-    public static final int NORWOOD_SYSTEMS = 0x002E;
-
-    /*
-     * MewTel Technology Inc.
-     */
-    public static final int MEWTEL_TECHNOLOGY = 0x002F;
-
-    /*
-     * ST Microelectronics.
-     */
-    public static final int ST_MICROELECTRONICS = 0x0030;
-
-    /*
-     * Synopsys.
-     */
-    public static final int SYNOPSYS = 0x0031;
-
-    /*
-     * Red-M (Communications) Ltd.
-     */
-    public static final int RED_M = 0x0032;
-
-    /*
-     * Commil Ltd.
-     */
-    public static final int COMMIL = 0x0033;
-
-    /*
-     * Computer Access Technology Corporation (CATC).
-     */
-    public static final int CATC = 0x0034;
-
-    /*
-     * Eclipse (HQ Espana) S.L.
-     */
-    public static final int ECLIPSE = 0x0035;
-
-    /*
-     * Renesas Technology Corp.
-     */
-    public static final int RENESAS_TECHNOLOGY = 0x0036;
-
-    /*
-     * Mobilian Corporation.
-     */
-    public static final int MOBILIAN_CORPORATION = 0x0037;
-
-    /*
-     * Terax.
-     */
-    public static final int TERAX = 0x0038;
-
-    /*
-     * Integrated System Solution Corp.
-     */
-    public static final int INTEGRATED_SYSTEM_SOLUTION = 0x0039;
-
-    /*
-     * Matsushita Electric Industrial Co., Ltd.
-     */
-    public static final int MATSUSHITA_ELECTRIC = 0x003A;
-
-    /*
-     * Gennum Corporation.
-     */
-    public static final int GENNUM = 0x003B;
-
-    /*
-     * Research In Motion.
-     */
-    public static final int RESEARCH_IN_MOTION = 0x003C;
-
-    /*
-     * IPextreme, Inc.
-     */
-    public static final int IPEXTREME = 0x003D;
-
-    /*
-     * Systems and Chips, Inc.
-     */
-    public static final int SYSTEMS_AND_CHIPS = 0x003E;
-
-    /*
-     * Bluetooth SIG, Inc.
-     */
-    public static final int BLUETOOTH_SIG = 0x003F;
-
-    /*
-     * Seiko Epson Corporation.
-     */
-    public static final int SEIKO_EPSON = 0x0040;
-
-    /*
-     * Integrated Silicon Solution Taiwan, Inc.
-     */
-    public static final int INTEGRATED_SILICON_SOLUTION = 0x0041;
-
-    /*
-     * CONWISE Technology Corporation Ltd.
-     */
-    public static final int CONWISE_TECHNOLOGY = 0x0042;
-
-    /*
-     * PARROT SA.
-     */
-    public static final int PARROT = 0x0043;
-
-    /*
-     * Socket Mobile.
-     */
-    public static final int SOCKET_MOBILE = 0x0044;
-
-    /*
-     * Atheros Communications, Inc.
-     */
-    public static final int ATHEROS_COMMUNICATIONS = 0x0045;
-
-    /*
-     * MediaTek, Inc.
-     */
-    public static final int MEDIATEK = 0x0046;
-
-    /*
-     * Bluegiga.
-     */
-    public static final int BLUEGIGA = 0x0047;
-
-    /*
-     * Marvell Technology Group Ltd.
-     */
-    public static final int MARVELL = 0x0048;
-
-    /*
-     * 3DSP Corporation.
-     */
-    public static final int THREE_DSP = 0x0049;
-
-    /*
-     * Accel Semiconductor Ltd.
-     */
-    public static final int ACCEL_SEMICONDUCTOR = 0x004A;
-
-    /*
-     * Continental Automotive Systems.
-     */
-    public static final int CONTINENTAL_AUTOMOTIVE = 0x004B;
-
-    /*
-     * Apple, Inc.
-     */
-    public static final int APPLE = 0x004C;
-
-    /*
-     * Staccato Communications, Inc.
-     */
-    public static final int STACCATO_COMMUNICATIONS = 0x004D;
-
-    /*
-     * Avago Technologies.
-     */
-    public static final int AVAGO = 0x004E;
-
-    /*
-     * APT Licensing Ltd.
-     */
-    public static final int APT_LICENSING = 0x004F;
-
-    /*
-     * SiRF Technology, Inc.
-     */
-    public static final int SIRF_TECHNOLOGY = 0x0050;
-
-    /*
-     * Tzero Technologies, Inc.
-     */
-    public static final int TZERO_TECHNOLOGIES = 0x0051;
-
-    /*
-     * J&M Corporation.
-     */
-    public static final int J_AND_M = 0x0052;
-
-    /*
-     * Free2move AB.
-     */
-    public static final int FREE2MOVE = 0x0053;
-
-    /*
-     * 3DiJoy Corporation.
-     */
-    public static final int THREE_DIJOY = 0x0054;
-
-    /*
-     * Plantronics, Inc.
-     */
-    public static final int PLANTRONICS = 0x0055;
-
-    /*
-     * Sony Ericsson Mobile Communications.
-     */
-    public static final int SONY_ERICSSON = 0x0056;
-
-    /*
-     * Harman International Industries, Inc.
-     */
-    public static final int HARMAN_INTERNATIONAL = 0x0057;
-
-    /*
-     * Vizio, Inc.
-     */
-    public static final int VIZIO = 0x0058;
-
-    /*
-     * Nordic Semiconductor ASA.
-     */
-    public static final int NORDIC_SEMICONDUCTOR = 0x0059;
-
-    /*
-     * EM Microelectronic-Marin SA.
-     */
-    public static final int EM_MICROELECTRONIC_MARIN = 0x005A;
-
-    /*
-     * Ralink Technology Corporation.
-     */
-    public static final int RALINK_TECHNOLOGY = 0x005B;
-
-    /*
-     * Belkin International, Inc.
-     */
-    public static final int BELKIN_INTERNATIONAL = 0x005C;
-
-    /*
-     * Realtek Semiconductor Corporation.
-     */
-    public static final int REALTEK_SEMICONDUCTOR = 0x005D;
-
-    /*
-     * Stonestreet One, LLC.
-     */
-    public static final int STONESTREET_ONE = 0x005E;
-
-    /*
-     * Wicentric, Inc.
-     */
-    public static final int WICENTRIC = 0x005F;
-
-    /*
-     * RivieraWaves S.A.S.
-     */
-    public static final int RIVIERAWAVES = 0x0060;
-
-    /*
-     * RDA Microelectronics.
-     */
-    public static final int RDA_MICROELECTRONICS = 0x0061;
-
-    /*
-     * Gibson Guitars.
-     */
-    public static final int GIBSON_GUITARS = 0x0062;
-
-    /*
-     * MiCommand Inc.
-     */
-    public static final int MICOMMAND = 0x0063;
-
-    /*
-     * Band XI International, LLC.
-     */
-    public static final int BAND_XI_INTERNATIONAL = 0x0064;
-
-    /*
-     * Hewlett-Packard Company.
-     */
-    public static final int HEWLETT_PACKARD = 0x0065;
-
-    /*
-     * 9Solutions Oy.
-     */
-    public static final int NINE_SOLUTIONS = 0x0066;
-
-    /*
-     * GN Netcom A/S.
-     */
-    public static final int GN_NETCOM = 0x0067;
-
-    /*
-     * General Motors.
-     */
-    public static final int GENERAL_MOTORS = 0x0068;
-
-    /*
-     * A&D Engineering, Inc.
-     */
-    public static final int A_AND_D_ENGINEERING = 0x0069;
-
-    /*
-     * MindTree Ltd.
-     */
-    public static final int MINDTREE = 0x006A;
-
-    /*
-     * Polar Electro OY.
-     */
-    public static final int POLAR_ELECTRO = 0x006B;
-
-    /*
-     * Beautiful Enterprise Co., Ltd.
-     */
-    public static final int BEAUTIFUL_ENTERPRISE = 0x006C;
-
-    /*
-     * BriarTek, Inc.
-     */
-    public static final int BRIARTEK = 0x006D;
-
-    /*
-     * Summit Data Communications, Inc.
-     */
-    public static final int SUMMIT_DATA_COMMUNICATIONS = 0x006E;
-
-    /*
-     * Sound ID.
-     */
-    public static final int SOUND_ID = 0x006F;
-
-    /*
-     * Monster, LLC.
-     */
-    public static final int MONSTER = 0x0070;
-
-    /*
-     * connectBlue AB.
-     */
-    public static final int CONNECTBLUE = 0x0071;
-
-    /*
-     * ShangHai Super Smart Electronics Co. Ltd.
-     */
-    public static final int SHANGHAI_SUPER_SMART_ELECTRONICS = 0x0072;
-
-    /*
-     * Group Sense Ltd.
-     */
-    public static final int GROUP_SENSE = 0x0073;
-
-    /*
-     * Zomm, LLC.
-     */
-    public static final int ZOMM = 0x0074;
-
-    /*
-     * Samsung Electronics Co. Ltd.
-     */
-    public static final int SAMSUNG_ELECTRONICS = 0x0075;
-
-    /*
-     * Creative Technology Ltd.
-     */
-    public static final int CREATIVE_TECHNOLOGY = 0x0076;
-
-    /*
-     * Laird Technologies.
-     */
-    public static final int LAIRD_TECHNOLOGIES = 0x0077;
-
-    /*
-     * Nike, Inc.
-     */
-    public static final int NIKE = 0x0078;
-
-    /*
-     * lesswire AG.
-     */
-    public static final int LESSWIRE = 0x0079;
-
-    /*
-     * MStar Semiconductor, Inc.
-     */
-    public static final int MSTAR_SEMICONDUCTOR = 0x007A;
-
-    /*
-     * Hanlynn Technologies.
-     */
-    public static final int HANLYNN_TECHNOLOGIES = 0x007B;
-
-    /*
-     * A & R Cambridge.
-     */
-    public static final int A_AND_R_CAMBRIDGE = 0x007C;
-
-    /*
-     * Seers Technology Co. Ltd.
-     */
-    public static final int SEERS_TECHNOLOGY = 0x007D;
-
-    /*
-     * Sports Tracking Technologies Ltd.
-     */
-    public static final int SPORTS_TRACKING_TECHNOLOGIES = 0x007E;
-
-    /*
-     * Autonet Mobile.
-     */
-    public static final int AUTONET_MOBILE = 0x007F;
-
-    /*
-     * DeLorme Publishing Company, Inc.
-     */
-    public static final int DELORME_PUBLISHING_COMPANY = 0x0080;
-
-    /*
-     * WuXi Vimicro.
-     */
-    public static final int WUXI_VIMICRO = 0x0081;
-
-    /*
-     * Sennheiser Communications A/S.
-     */
-    public static final int SENNHEISER_COMMUNICATIONS = 0x0082;
-
-    /*
-     * TimeKeeping Systems, Inc.
-     */
-    public static final int TIMEKEEPING_SYSTEMS = 0x0083;
-
-    /*
-     * Ludus Helsinki Ltd.
-     */
-    public static final int LUDUS_HELSINKI = 0x0084;
-
-    /*
-     * BlueRadios, Inc.
-     */
-    public static final int BLUERADIOS = 0x0085;
-
-    /*
-     * equinox AG.
-     */
-    public static final int EQUINOX_AG = 0x0086;
-
-    /*
-     * Garmin International, Inc.
-     */
-    public static final int GARMIN_INTERNATIONAL = 0x0087;
-
-    /*
-     * Ecotest.
-     */
-    public static final int ECOTEST = 0x0088;
-
-    /*
-     * GN ReSound A/S.
-     */
-    public static final int GN_RESOUND = 0x0089;
-
-    /*
-     * Jawbone.
-     */
-    public static final int JAWBONE = 0x008A;
-
-    /*
-     * Topcorn Positioning Systems, LLC.
-     */
-    public static final int TOPCORN_POSITIONING_SYSTEMS = 0x008B;
-
-    /*
-     * Qualcomm Labs, Inc.
-     */
-    public static final int QUALCOMM_LABS = 0x008C;
-
-    /*
-     * Zscan Software.
-     */
-    public static final int ZSCAN_SOFTWARE = 0x008D;
-
-    /*
-     * Quintic Corp.
-     */
-    public static final int QUINTIC = 0x008E;
-
-    /*
-     * Stollman E+V GmbH.
-     */
-    public static final int STOLLMAN_E_PLUS_V = 0x008F;
-
-    /*
-     * Funai Electric Co., Ltd.
-     */
-    public static final int FUNAI_ELECTRIC = 0x0090;
-
-    /*
-     * Advanced PANMOBIL Systems GmbH & Co. KG.
-     */
-    public static final int ADVANCED_PANMOBIL_SYSTEMS = 0x0091;
-
-    /*
-     * ThinkOptics, Inc.
-     */
-    public static final int THINKOPTICS = 0x0092;
-
-    /*
-     * Universal Electronics, Inc.
-     */
-    public static final int UNIVERSAL_ELECTRONICS = 0x0093;
-
-    /*
-     * Airoha Technology Corp.
-     */
-    public static final int AIROHA_TECHNOLOGY = 0x0094;
-
-    /*
-     * NEC Lighting, Ltd.
-     */
-    public static final int NEC_LIGHTING = 0x0095;
-
-    /*
-     * ODM Technology, Inc.
-     */
-    public static final int ODM_TECHNOLOGY = 0x0096;
-
-    /*
-     * Bluetrek Technologies Limited.
-     */
-    public static final int BLUETREK_TECHNOLOGIES = 0x0097;
-
-    /*
-     * zer01.tv GmbH.
-     */
-    public static final int ZER01_TV = 0x0098;
-
-    /*
-     * i.Tech Dynamic Global Distribution Ltd.
-     */
-    public static final int I_TECH_DYNAMIC_GLOBAL_DISTRIBUTION = 0x0099;
-
-    /*
-     * Alpwise.
-     */
-    public static final int ALPWISE = 0x009A;
-
-    /*
-     * Jiangsu Toppower Automotive Electronics Co., Ltd.
-     */
-    public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS = 0x009B;
-
-    /*
-     * Colorfy, Inc.
-     */
-    public static final int COLORFY = 0x009C;
-
-    /*
-     * Geoforce Inc.
-     */
-    public static final int GEOFORCE = 0x009D;
-
-    /*
-     * Bose Corporation.
-     */
-    public static final int BOSE = 0x009E;
-
-    /*
-     * Suunto Oy.
-     */
-    public static final int SUUNTO = 0x009F;
-
-    /*
-     * Kensington Computer Products Group.
-     */
-    public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0;
-
-    /*
-     * SR-Medizinelektronik.
-     */
-    public static final int SR_MEDIZINELEKTRONIK = 0x00A1;
-
-    /*
-     * Vertu Corporation Limited.
-     */
-    public static final int VERTU = 0x00A2;
-
-    /*
-     * Meta Watch Ltd.
-     */
-    public static final int META_WATCH = 0x00A3;
-
-    /*
-     * LINAK A/S.
-     */
-    public static final int LINAK = 0x00A4;
-
-    /*
-     * OTL Dynamics LLC.
-     */
-    public static final int OTL_DYNAMICS = 0x00A5;
-
-    /*
-     * Panda Ocean Inc.
-     */
-    public static final int PANDA_OCEAN = 0x00A6;
-
-    /*
-     * Visteon Corporation.
-     */
-    public static final int VISTEON = 0x00A7;
-
-    /*
-     * ARP Devices Limited.
-     */
-    public static final int ARP_DEVICES = 0x00A8;
-
-    /*
-     * Magneti Marelli S.p.A.
-     */
-    public static final int MAGNETI_MARELLI = 0x00A9;
-
-    /*
-     * CAEN RFID srl.
-     */
-    public static final int CAEN_RFID = 0x00AA;
-
-    /*
-     * Ingenieur-Systemgruppe Zahn GmbH.
-     */
-    public static final int INGENIEUR_SYSTEMGRUPPE_ZAHN = 0x00AB;
-
-    /*
-     * Green Throttle Games.
-     */
-    public static final int GREEN_THROTTLE_GAMES = 0x00AC;
-
-    /*
-     * Peter Systemtechnik GmbH.
-     */
-    public static final int PETER_SYSTEMTECHNIK = 0x00AD;
-
-    /*
-     * Omegawave Oy.
-     */
-    public static final int OMEGAWAVE = 0x00AE;
-
-    /*
-     * Cinetix.
-     */
-    public static final int CINETIX = 0x00AF;
-
-    /*
-     * Passif Semiconductor Corp.
-     */
-    public static final int PASSIF_SEMICONDUCTOR = 0x00B0;
-
-    /*
-     * Saris Cycling Group, Inc.
-     */
-    public static final int SARIS_CYCLING_GROUP = 0x00B1;
-
-    /*
-     * Bekey A/S.
-     */
-    public static final int BEKEY = 0x00B2;
-
-    /*
-     * Clarinox Technologies Pty. Ltd.
-     */
-    public static final int CLARINOX_TECHNOLOGIES = 0x00B3;
-
-    /*
-     * BDE Technology Co., Ltd.
-     */
-    public static final int BDE_TECHNOLOGY = 0x00B4;
-
-    /*
-     * Swirl Networks.
-     */
-    public static final int SWIRL_NETWORKS = 0x00B5;
-
-    /*
-     * Meso international.
-     */
-    public static final int MESO_INTERNATIONAL = 0x00B6;
-
-    /*
-     * TreLab Ltd.
-     */
-    public static final int TRELAB = 0x00B7;
-
-    /*
-     * Qualcomm Innovation Center, Inc. (QuIC).
-     */
-    public static final int QUALCOMM_INNOVATION_CENTER = 0x00B8;
-
-    /*
-     * Johnson Controls, Inc.
-     */
-    public static final int JOHNSON_CONTROLS = 0x00B9;
-
-    /*
-     * Starkey Laboratories Inc.
-     */
-    public static final int STARKEY_LABORATORIES = 0x00BA;
-
-    /*
-     * S-Power Electronics Limited.
-     */
-    public static final int S_POWER_ELECTRONICS = 0x00BB;
-
-    /*
-     * Ace Sensor Inc.
-     */
-    public static final int ACE_SENSOR = 0x00BC;
-
-    /*
-     * Aplix Corporation.
-     */
-    public static final int APLIX = 0x00BD;
-
-    /*
-     * AAMP of America.
-     */
-    public static final int AAMP_OF_AMERICA = 0x00BE;
-
-    /*
-     * Stalmart Technology Limited.
-     */
-    public static final int STALMART_TECHNOLOGY = 0x00BF;
-
-    /*
-     * AMICCOM Electronics Corporation.
-     */
-    public static final int AMICCOM_ELECTRONICS = 0x00C0;
-
-    /*
-     * Shenzhen Excelsecu Data Technology Co.,Ltd.
-     */
-    public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY = 0x00C1;
-
-    /*
-     * Geneq Inc.
-     */
-    public static final int GENEQ = 0x00C2;
-
-    /*
-     * adidas AG.
-     */
-    public static final int ADIDAS = 0x00C3;
-
-    /*
-     * LG Electronics.
-     */
-    public static final int LG_ELECTRONICS = 0x00C4;
-
-    /*
-     * Onset Computer Corporation.
-     */
-    public static final int ONSET_COMPUTER = 0x00C5;
-
-    /*
-     * Selfly BV.
-     */
-    public static final int SELFLY = 0x00C6;
-
-    /*
-     * Quuppa Oy.
-     */
-    public static final int QUUPPA = 0x00C7;
-
-    /*
-     * GeLo Inc.
-     */
-    public static final int GELO = 0x00C8;
-
-    /*
-     * Evluma.
-     */
-    public static final int EVLUMA = 0x00C9;
-
-    /*
-     * MC10.
-     */
-    public static final int MC10 = 0x00CA;
-
-    /*
-     * Binauric SE.
-     */
-    public static final int BINAURIC = 0x00CB;
-
-    /*
-     * Beats Electronics.
-     */
-    public static final int BEATS_ELECTRONICS = 0x00CC;
-
-    /*
-     * Microchip Technology Inc.
-     */
-    public static final int MICROCHIP_TECHNOLOGY = 0x00CD;
-
-    /*
-     * Elgato Systems GmbH.
-     */
-    public static final int ELGATO_SYSTEMS = 0x00CE;
-
-    /*
-     * ARCHOS SA.
-     */
-    public static final int ARCHOS = 0x00CF;
-
-    /*
-     * Dexcom, Inc.
-     */
-    public static final int DEXCOM = 0x00D0;
-
-    /*
-     * Polar Electro Europe B.V.
-     */
-    public static final int POLAR_ELECTRO_EUROPE = 0x00D1;
-
-    /*
-     * Dialog Semiconductor B.V.
-     */
-    public static final int DIALOG_SEMICONDUCTOR = 0x00D2;
-
-    /*
-     * Taixingbang Technology (HK) Co,. LTD.
-     */
-    public static final int TAIXINGBANG_TECHNOLOGY = 0x00D3;
-
-    /*
-     * Kawantech.
-     */
-    public static final int KAWANTECH = 0x00D4;
-
-    /*
-     * Austco Communication Systems.
-     */
-    public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5;
-
-    /*
-     * Timex Group USA, Inc.
-     */
-    public static final int TIMEX_GROUP_USA = 0x00D6;
-
-    /*
-     * Qualcomm Technologies, Inc.
-     */
-    public static final int QUALCOMM_TECHNOLOGIES = 0x00D7;
-
-    /*
-     * Qualcomm Connected Experiences, Inc.
-     */
-    public static final int QUALCOMM_CONNECTED_EXPERIENCES = 0x00D8;
-
-    /*
-     * Voyetra Turtle Beach.
-     */
-    public static final int VOYETRA_TURTLE_BEACH = 0x00D9;
-
-    /*
-     * txtr GmbH.
-     */
-    public static final int TXTR = 0x00DA;
-
-    /*
-     * Biosentronics.
-     */
-    public static final int BIOSENTRONICS = 0x00DB;
-
-    /*
-     * Procter & Gamble.
-     */
-    public static final int PROCTER_AND_GAMBLE = 0x00DC;
-
-    /*
-     * Hosiden Corporation.
-     */
-    public static final int HOSIDEN = 0x00DD;
-
-    /*
-     * Muzik LLC.
-     */
-    public static final int MUZIK = 0x00DE;
-
-    /*
-     * Misfit Wearables Corp.
-     */
-    public static final int MISFIT_WEARABLES = 0x00DF;
-
-    /*
-     * Google.
-     */
-    public static final int GOOGLE = 0x00E0;
-
-    /*
-     * Danlers Ltd.
-     */
-    public static final int DANLERS = 0x00E1;
-
-    /*
-     * Semilink Inc.
-     */
-    public static final int SEMILINK = 0x00E2;
-
-    /*
-     * You can't instantiate one of these.
-     */
-    private BluetoothAssignedNumbers() {
-    }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java
deleted file mode 100644
index 4c8b8c1..0000000
--- a/core/java/android/bluetooth/BluetoothAudioConfig.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents the audio configuration for a Bluetooth A2DP source device.
- *
- * {@see BluetoothA2dpSink}
- *
- * {@hide}
- */
-public final class BluetoothAudioConfig implements Parcelable {
-
-    private final int mSampleRate;
-    private final int mChannelConfig;
-    private final int mAudioFormat;
-
-    public BluetoothAudioConfig(int sampleRate, int channelConfig, int audioFormat) {
-        mSampleRate = sampleRate;
-        mChannelConfig = channelConfig;
-        mAudioFormat = audioFormat;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothAudioConfig) {
-            BluetoothAudioConfig bac = (BluetoothAudioConfig) o;
-            return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig
-                    && bac.mAudioFormat == mAudioFormat);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mSampleRate | (mChannelConfig << 24) | (mAudioFormat << 28);
-    }
-
-    @Override
-    public String toString() {
-        return "{mSampleRate:" + mSampleRate + ",mChannelConfig:" + mChannelConfig
-                + ",mAudioFormat:" + mAudioFormat + "}";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothAudioConfig> CREATOR =
-            new Parcelable.Creator<BluetoothAudioConfig>() {
-                public BluetoothAudioConfig createFromParcel(Parcel in) {
-                    int sampleRate = in.readInt();
-                    int channelConfig = in.readInt();
-                    int audioFormat = in.readInt();
-                    return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat);
-                }
-
-                public BluetoothAudioConfig[] newArray(int size) {
-                    return new BluetoothAudioConfig[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSampleRate);
-        out.writeInt(mChannelConfig);
-        out.writeInt(mAudioFormat);
-    }
-
-    /**
-     * Returns the sample rate in samples per second
-     *
-     * @return sample rate
-     */
-    public int getSampleRate() {
-        return mSampleRate;
-    }
-
-    /**
-     * Returns the channel configuration (either {@link android.media.AudioFormat#CHANNEL_IN_MONO}
-     * or {@link android.media.AudioFormat#CHANNEL_IN_STEREO})
-     *
-     * @return channel configuration
-     */
-    public int getChannelConfig() {
-        return mChannelConfig;
-    }
-
-    /**
-     * Returns the channel audio format (either {@link android.media.AudioFormat#ENCODING_PCM_16BIT}
-     * or {@link android.media.AudioFormat#ENCODING_PCM_8BIT}
-     *
-     * @return audio format
-     */
-    public int getAudioFormat() {
-        return mAudioFormat;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAvrcp.java b/core/java/android/bluetooth/BluetoothAvrcp.java
deleted file mode 100644
index 1a4c759..0000000
--- a/core/java/android/bluetooth/BluetoothAvrcp.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-/**
- * This class contains constants for Bluetooth AVRCP profile.
- *
- * {@hide}
- */
-public final class BluetoothAvrcp {
-
-    /*
-     * State flags for Passthrough commands
-    */
-    public static final int PASSTHROUGH_STATE_PRESS = 0;
-    public static final int PASSTHROUGH_STATE_RELEASE = 1;
-
-    /*
-     * Operation IDs for Passthrough commands
-    */
-    public static final int PASSTHROUGH_ID_SELECT = 0x00;    /* select */
-    public static final int PASSTHROUGH_ID_UP = 0x01;    /* up */
-    public static final int PASSTHROUGH_ID_DOWN = 0x02;    /* down */
-    public static final int PASSTHROUGH_ID_LEFT = 0x03;    /* left */
-    public static final int PASSTHROUGH_ID_RIGHT = 0x04;    /* right */
-    public static final int PASSTHROUGH_ID_RIGHT_UP = 0x05;    /* right-up */
-    public static final int PASSTHROUGH_ID_RIGHT_DOWN = 0x06;    /* right-down */
-    public static final int PASSTHROUGH_ID_LEFT_UP = 0x07;    /* left-up */
-    public static final int PASSTHROUGH_ID_LEFT_DOWN = 0x08;    /* left-down */
-    public static final int PASSTHROUGH_ID_ROOT_MENU = 0x09;    /* root menu */
-    public static final int PASSTHROUGH_ID_SETUP_MENU = 0x0A;    /* setup menu */
-    public static final int PASSTHROUGH_ID_CONT_MENU = 0x0B;    /* contents menu */
-    public static final int PASSTHROUGH_ID_FAV_MENU = 0x0C;    /* favorite menu */
-    public static final int PASSTHROUGH_ID_EXIT = 0x0D;    /* exit */
-    public static final int PASSTHROUGH_ID_0 = 0x20;    /* 0 */
-    public static final int PASSTHROUGH_ID_1 = 0x21;    /* 1 */
-    public static final int PASSTHROUGH_ID_2 = 0x22;    /* 2 */
-    public static final int PASSTHROUGH_ID_3 = 0x23;    /* 3 */
-    public static final int PASSTHROUGH_ID_4 = 0x24;    /* 4 */
-    public static final int PASSTHROUGH_ID_5 = 0x25;    /* 5 */
-    public static final int PASSTHROUGH_ID_6 = 0x26;    /* 6 */
-    public static final int PASSTHROUGH_ID_7 = 0x27;    /* 7 */
-    public static final int PASSTHROUGH_ID_8 = 0x28;    /* 8 */
-    public static final int PASSTHROUGH_ID_9 = 0x29;    /* 9 */
-    public static final int PASSTHROUGH_ID_DOT = 0x2A;    /* dot */
-    public static final int PASSTHROUGH_ID_ENTER = 0x2B;    /* enter */
-    public static final int PASSTHROUGH_ID_CLEAR = 0x2C;    /* clear */
-    public static final int PASSTHROUGH_ID_CHAN_UP = 0x30;    /* channel up */
-    public static final int PASSTHROUGH_ID_CHAN_DOWN = 0x31;    /* channel down */
-    public static final int PASSTHROUGH_ID_PREV_CHAN = 0x32;    /* previous channel */
-    public static final int PASSTHROUGH_ID_SOUND_SEL = 0x33;    /* sound select */
-    public static final int PASSTHROUGH_ID_INPUT_SEL = 0x34;    /* input select */
-    public static final int PASSTHROUGH_ID_DISP_INFO = 0x35;    /* display information */
-    public static final int PASSTHROUGH_ID_HELP = 0x36;    /* help */
-    public static final int PASSTHROUGH_ID_PAGE_UP = 0x37;    /* page up */
-    public static final int PASSTHROUGH_ID_PAGE_DOWN = 0x38;    /* page down */
-    public static final int PASSTHROUGH_ID_POWER = 0x40;    /* power */
-    public static final int PASSTHROUGH_ID_VOL_UP = 0x41;    /* volume up */
-    public static final int PASSTHROUGH_ID_VOL_DOWN = 0x42;    /* volume down */
-    public static final int PASSTHROUGH_ID_MUTE = 0x43;    /* mute */
-    public static final int PASSTHROUGH_ID_PLAY = 0x44;    /* play */
-    public static final int PASSTHROUGH_ID_STOP = 0x45;    /* stop */
-    public static final int PASSTHROUGH_ID_PAUSE = 0x46;    /* pause */
-    public static final int PASSTHROUGH_ID_RECORD = 0x47;    /* record */
-    public static final int PASSTHROUGH_ID_REWIND = 0x48;    /* rewind */
-    public static final int PASSTHROUGH_ID_FAST_FOR = 0x49;    /* fast forward */
-    public static final int PASSTHROUGH_ID_EJECT = 0x4A;    /* eject */
-    public static final int PASSTHROUGH_ID_FORWARD = 0x4B;    /* forward */
-    public static final int PASSTHROUGH_ID_BACKWARD = 0x4C;    /* backward */
-    public static final int PASSTHROUGH_ID_ANGLE = 0x50;    /* angle */
-    public static final int PASSTHROUGH_ID_SUBPICT = 0x51;    /* subpicture */
-    public static final int PASSTHROUGH_ID_F1 = 0x71;    /* F1 */
-    public static final int PASSTHROUGH_ID_F2 = 0x72;    /* F2 */
-    public static final int PASSTHROUGH_ID_F3 = 0x73;    /* F3 */
-    public static final int PASSTHROUGH_ID_F4 = 0x74;    /* F4 */
-    public static final int PASSTHROUGH_ID_F5 = 0x75;    /* F5 */
-    public static final int PASSTHROUGH_ID_VENDOR = 0x7E;    /* vendor unique */
-    public static final int PASSTHROUGH_KEYPRESSED_RELEASE = 0x80;
-}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
deleted file mode 100644
index 81fc3e1..0000000
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
- * supports player information, playback support and track metadata.
- *
- * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothAvrcpController proxy object.
- *
- * {@hide}
- */
-public final class BluetoothAvrcpController implements BluetoothProfile {
-    private static final String TAG = "BluetoothAvrcpController";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the AVRCP Controller
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in player application setting state on AVRCP AG.
-     *
-     * <p>This intent will have the following extras:
-     * <ul>
-     * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
-     * most recent player setting. </li>
-     * </ul>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PLAYER_SETTING =
-            "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
-
-    public static final String EXTRA_PLAYER_SETTING =
-            "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER,
-                    "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
-                @Override
-                public IBluetoothAvrcpController getServiceInterface(IBinder service) {
-                    return IBluetoothAvrcpController.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothAvrcpController proxy object for interacting with the local
-     * Bluetooth AVRCP service.
-     */
-    /* package */ BluetoothAvrcpController(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothAvrcpController getService() {
-        return mProfileConnector.getService();
-    }
-
-    @Override
-    public void finalize() {
-        close();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothAvrcpController service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothAvrcpController service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothAvrcpController service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Gets the player application settings.
-     *
-     * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getPlayerSettings");
-        BluetoothAvrcpPlayerSettings settings = null;
-        final IBluetoothAvrcpController service = getService();
-        final BluetoothAvrcpPlayerSettings defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv =
-                        new SynchronousResultReceiver();
-                service.getPlayerSettings(device, mAttributionSource, recv);
-                settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets the player app setting for current player.
-     * returns true in case setting is supported by remote, false otherwise
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
-        if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
-        final IBluetoothAvrcpController service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Group Navigation Command to Remote.
-     * possible keycode values: next_grp, previous_grp defined above
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
-        Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
-                + keyState);
-        final IBluetoothAvrcpController service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-                return;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
deleted file mode 100644
index 30aea1a..0000000
--- a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2015 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Class used to identify settings associated with the player on AG.
- *
- * {@hide}
- */
-public final class BluetoothAvrcpPlayerSettings implements Parcelable {
-    public static final String TAG = "BluetoothAvrcpPlayerSettings";
-
-    /**
-     * Equalizer setting.
-     */
-    public static final int SETTING_EQUALIZER = 0x01;
-
-    /**
-     * Repeat setting.
-     */
-    public static final int SETTING_REPEAT = 0x02;
-
-    /**
-     * Shuffle setting.
-     */
-    public static final int SETTING_SHUFFLE = 0x04;
-
-    /**
-     * Scan mode setting.
-     */
-    public static final int SETTING_SCAN = 0x08;
-
-    /**
-     * Invalid state.
-     *
-     * Used for returning error codes.
-     */
-    public static final int STATE_INVALID = -1;
-
-    /**
-     * OFF state.
-     *
-     * Denotes a general OFF state. Applies to all settings.
-     */
-    public static final int STATE_OFF = 0x00;
-
-    /**
-     * ON state.
-     *
-     * Applies to {@link SETTING_EQUALIZER}.
-     */
-    public static final int STATE_ON = 0x01;
-
-    /**
-     * Single track repeat.
-     *
-     * Applies only to {@link SETTING_REPEAT}.
-     */
-    public static final int STATE_SINGLE_TRACK = 0x02;
-
-    /**
-     * All track repeat/shuffle.
-     *
-     * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}.
-     */
-    public static final int STATE_ALL_TRACK = 0x03;
-
-    /**
-     * Group repeat/shuffle.
-     *
-     * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}.
-     */
-    public static final int STATE_GROUP = 0x04;
-
-    /**
-     * List of supported settings ORed.
-     */
-    private int mSettings;
-
-    /**
-     * Hash map of current capability values.
-     */
-    private Map<Integer, Integer> mSettingsValue = new HashMap<Integer, Integer>();
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSettings);
-        out.writeInt(mSettingsValue.size());
-        for (int k : mSettingsValue.keySet()) {
-            out.writeInt(k);
-            out.writeInt(mSettingsValue.get(k));
-        }
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothAvrcpPlayerSettings> CREATOR =
-            new Parcelable.Creator<BluetoothAvrcpPlayerSettings>() {
-        public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) {
-            return new BluetoothAvrcpPlayerSettings(in);
-        }
-
-        public BluetoothAvrcpPlayerSettings[] newArray(int size) {
-            return new BluetoothAvrcpPlayerSettings[size];
-        }
-    };
-
-    private BluetoothAvrcpPlayerSettings(Parcel in) {
-        mSettings = in.readInt();
-        int numSettings = in.readInt();
-        for (int i = 0; i < numSettings; i++) {
-            mSettingsValue.put(in.readInt(), in.readInt());
-        }
-    }
-
-    /**
-     * Create a new player settings object.
-     *
-     * @param settings a ORed value of SETTINGS_* defined above.
-     */
-    public BluetoothAvrcpPlayerSettings(int settings) {
-        mSettings = settings;
-    }
-
-    /**
-     * Get the supported settings.
-     *
-     * @return int ORed value of supported settings.
-     */
-    public int getSettings() {
-        return mSettings;
-    }
-
-    /**
-     * Add a setting value.
-     *
-     * The setting must be part of possible settings in {@link getSettings()}.
-     *
-     * @param setting setting config.
-     * @param value value for the setting.
-     * @throws IllegalStateException if the setting is not supported.
-     */
-    public void addSettingValue(int setting, int value) {
-        if ((setting & mSettings) == 0) {
-            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
-            throw new IllegalStateException("Setting not supported: " + setting);
-        }
-        mSettingsValue.put(setting, value);
-    }
-
-    /**
-     * Get a setting value.
-     *
-     * The setting must be part of possible settings in {@link getSettings()}.
-     *
-     * @param setting setting config.
-     * @return value value for the setting.
-     * @throws IllegalStateException if the setting is not supported.
-     */
-    public int getSettingValue(int setting) {
-        if ((setting & mSettings) == 0) {
-            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
-            throw new IllegalStateException("Setting not supported: " + setting);
-        }
-        Integer i = mSettingsValue.get(setting);
-        if (i == null) return -1;
-        return i;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
deleted file mode 100755
index a3c45d0..0000000
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-
-/**
- * Represents a Bluetooth class, which describes general characteristics
- * and capabilities of a device. For example, a Bluetooth class will
- * specify the general device type such as a phone, a computer, or
- * headset, and whether it's capable of services such as audio or telephony.
- *
- * <p>Every Bluetooth class is composed of zero or more service classes, and
- * exactly one device class. The device class is further broken down into major
- * and minor device class components.
- *
- * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device
- * (for example to show an icon in the UI), but does not reliably describe which
- * Bluetooth profiles or services are actually supported by a device. Accurate
- * service discovery is done through SDP requests, which are automatically
- * performed when creating an RFCOMM socket with {@link
- * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link
- * BluetoothAdapter#listenUsingRfcommWithServiceRecord}</p>
- *
- * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
- * a remote device.
- *
- * <!--
- * The Bluetooth class is a 32 bit field. The format of these bits is defined at
- * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
- * (login required). This class contains that 32 bit field, and provides
- * constants and methods to determine which Service Class(es) and Device Class
- * are encoded in that field.
- * -->
- */
-public final class BluetoothClass implements Parcelable {
-    /**
-     * Legacy error value. Applications should use null instead.
-     *
-     * @hide
-     */
-    public static final int ERROR = 0xFF000000;
-
-    private final int mClass;
-
-    /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public BluetoothClass(int classInt) {
-        mClass = classInt;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothClass) {
-            return mClass == ((BluetoothClass) o).mClass;
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mClass;
-    }
-
-    @Override
-    public String toString() {
-        return Integer.toHexString(mClass);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothClass> CREATOR =
-            new Parcelable.Creator<BluetoothClass>() {
-                public BluetoothClass createFromParcel(Parcel in) {
-                    return new BluetoothClass(in.readInt());
-                }
-
-                public BluetoothClass[] newArray(int size) {
-                    return new BluetoothClass[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mClass);
-    }
-
-    /**
-     * Defines all service class constants.
-     * <p>Each {@link BluetoothClass} encodes zero or more service classes.
-     */
-    public static final class Service {
-        private static final int BITMASK = 0xFFE000;
-
-        public static final int LIMITED_DISCOVERABILITY = 0x002000;
-        public static final int LE_AUDIO = 0x004000;
-        public static final int POSITIONING = 0x010000;
-        public static final int NETWORKING = 0x020000;
-        public static final int RENDER = 0x040000;
-        public static final int CAPTURE = 0x080000;
-        public static final int OBJECT_TRANSFER = 0x100000;
-        public static final int AUDIO = 0x200000;
-        public static final int TELEPHONY = 0x400000;
-        public static final int INFORMATION = 0x800000;
-    }
-
-    /**
-     * Return true if the specified service class is supported by this
-     * {@link BluetoothClass}.
-     * <p>Valid service classes are the public constants in
-     * {@link BluetoothClass.Service}. For example, {@link
-     * BluetoothClass.Service#AUDIO}.
-     *
-     * @param service valid service class
-     * @return true if the service class is supported
-     */
-    public boolean hasService(int service) {
-        return ((mClass & Service.BITMASK & service) != 0);
-    }
-
-    /**
-     * Defines all device class constants.
-     * <p>Each {@link BluetoothClass} encodes exactly one device class, with
-     * major and minor components.
-     * <p>The constants in {@link
-     * BluetoothClass.Device} represent a combination of major and minor
-     * device components (the complete device class). The constants in {@link
-     * BluetoothClass.Device.Major} represent only major device classes.
-     * <p>See {@link BluetoothClass.Service} for service class constants.
-     */
-    public static class Device {
-        private static final int BITMASK = 0x1FFC;
-
-        /**
-         * Defines all major device class constants.
-         * <p>See {@link BluetoothClass.Device} for minor classes.
-         */
-        public static class Major {
-            private static final int BITMASK = 0x1F00;
-
-            public static final int MISC = 0x0000;
-            public static final int COMPUTER = 0x0100;
-            public static final int PHONE = 0x0200;
-            public static final int NETWORKING = 0x0300;
-            public static final int AUDIO_VIDEO = 0x0400;
-            public static final int PERIPHERAL = 0x0500;
-            public static final int IMAGING = 0x0600;
-            public static final int WEARABLE = 0x0700;
-            public static final int TOY = 0x0800;
-            public static final int HEALTH = 0x0900;
-            public static final int UNCATEGORIZED = 0x1F00;
-        }
-
-        // Devices in the COMPUTER major class
-        public static final int COMPUTER_UNCATEGORIZED = 0x0100;
-        public static final int COMPUTER_DESKTOP = 0x0104;
-        public static final int COMPUTER_SERVER = 0x0108;
-        public static final int COMPUTER_LAPTOP = 0x010C;
-        public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110;
-        public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114;
-        public static final int COMPUTER_WEARABLE = 0x0118;
-
-        // Devices in the PHONE major class
-        public static final int PHONE_UNCATEGORIZED = 0x0200;
-        public static final int PHONE_CELLULAR = 0x0204;
-        public static final int PHONE_CORDLESS = 0x0208;
-        public static final int PHONE_SMART = 0x020C;
-        public static final int PHONE_MODEM_OR_GATEWAY = 0x0210;
-        public static final int PHONE_ISDN = 0x0214;
-
-        // Minor classes for the AUDIO_VIDEO major class
-        public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400;
-        public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404;
-        public static final int AUDIO_VIDEO_HANDSFREE = 0x0408;
-        //public static final int AUDIO_VIDEO_RESERVED              = 0x040C;
-        public static final int AUDIO_VIDEO_MICROPHONE = 0x0410;
-        public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414;
-        public static final int AUDIO_VIDEO_HEADPHONES = 0x0418;
-        public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C;
-        public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420;
-        public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424;
-        public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428;
-        public static final int AUDIO_VIDEO_VCR = 0x042C;
-        public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430;
-        public static final int AUDIO_VIDEO_CAMCORDER = 0x0434;
-        public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438;
-        public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C;
-        public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440;
-        //public static final int AUDIO_VIDEO_RESERVED              = 0x0444;
-        public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448;
-
-        // Devices in the WEARABLE major class
-        public static final int WEARABLE_UNCATEGORIZED = 0x0700;
-        public static final int WEARABLE_WRIST_WATCH = 0x0704;
-        public static final int WEARABLE_PAGER = 0x0708;
-        public static final int WEARABLE_JACKET = 0x070C;
-        public static final int WEARABLE_HELMET = 0x0710;
-        public static final int WEARABLE_GLASSES = 0x0714;
-
-        // Devices in the TOY major class
-        public static final int TOY_UNCATEGORIZED = 0x0800;
-        public static final int TOY_ROBOT = 0x0804;
-        public static final int TOY_VEHICLE = 0x0808;
-        public static final int TOY_DOLL_ACTION_FIGURE = 0x080C;
-        public static final int TOY_CONTROLLER = 0x0810;
-        public static final int TOY_GAME = 0x0814;
-
-        // Devices in the HEALTH major class
-        public static final int HEALTH_UNCATEGORIZED = 0x0900;
-        public static final int HEALTH_BLOOD_PRESSURE = 0x0904;
-        public static final int HEALTH_THERMOMETER = 0x0908;
-        public static final int HEALTH_WEIGHING = 0x090C;
-        public static final int HEALTH_GLUCOSE = 0x0910;
-        public static final int HEALTH_PULSE_OXIMETER = 0x0914;
-        public static final int HEALTH_PULSE_RATE = 0x0918;
-        public static final int HEALTH_DATA_DISPLAY = 0x091C;
-
-        // Devices in PERIPHERAL major class
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_NON_KEYBOARD_NON_POINTING = 0x0500;
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_KEYBOARD = 0x0540;
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_POINTING = 0x0580;
-        /**
-         * @hide
-         */
-        public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0;
-    }
-
-    /**
-     * Return the major device class component of this {@link BluetoothClass}.
-     * <p>Values returned from this function can be compared with the
-     * public constants in {@link BluetoothClass.Device.Major} to determine
-     * which major class is encoded in this Bluetooth class.
-     *
-     * @return major device class component
-     */
-    public int getMajorDeviceClass() {
-        return (mClass & Device.Major.BITMASK);
-    }
-
-    /**
-     * Return the (major and minor) device class component of this
-     * {@link BluetoothClass}.
-     * <p>Values returned from this function can be compared with the
-     * public constants in {@link BluetoothClass.Device} to determine which
-     * device class is encoded in this Bluetooth class.
-     *
-     * @return device class component
-     */
-    public int getDeviceClass() {
-        return (mClass & Device.BITMASK);
-    }
-
-    /**
-     * Return the Bluetooth Class of Device (CoD) value including the
-     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
-     * minor device fields.
-     *
-     * <p>This value is an integer representation of Bluetooth CoD as in
-     * Bluetooth specification.
-     *
-     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
-     *
-     * @hide
-     */
-    @TestApi
-    public int getClassOfDevice() {
-        return mClass;
-    }
-
-    /**
-     * Return the Bluetooth Class of Device (CoD) value including the
-     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
-     * minor device fields.
-     *
-     * <p>This value is a byte array representation of Bluetooth CoD as in
-     * Bluetooth specification.
-     *
-     * <p>Bluetooth COD information is 3 bytes, but stored as an int. Hence the
-     * MSB is useless and needs to be thrown away. The lower 3 bytes are
-     * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN.
-     *
-     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
-     *
-     * @hide
-     */
-    public byte[] getClassOfDeviceBytes() {
-        byte[] bytes = ByteBuffer.allocate(4)
-                .order(ByteOrder.BIG_ENDIAN)
-                .putInt(mClass)
-                .array();
-
-        // Discard the top byte
-        return Arrays.copyOfRange(bytes, 1, bytes.length);
-    }
-
-    public static final int PROFILE_HEADSET = 0;
-
-    public static final int PROFILE_A2DP = 1;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_OPP = 2;
-
-    public static final int PROFILE_HID = 3;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_PANU = 4;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_NAP = 5;
-
-    /** @hide */
-    @SystemApi
-    public static final int PROFILE_A2DP_SINK = 6;
-
-    /**
-     * Check class bits for possible bluetooth profile support.
-     * This is a simple heuristic that tries to guess if a device with the
-     * given class bits might support specified profile. It is not accurate for all
-     * devices. It tries to err on the side of false positives.
-     *
-     * @param profile the profile to be checked
-     * @return whether this device supports specified profile
-     */
-    public boolean doesClassMatch(int profile) {
-        if (profile == PROFILE_A2DP) {
-            if (hasService(Service.RENDER)) {
-                return true;
-            }
-            // By the A2DP spec, sinks must indicate the RENDER service.
-            // However we found some that do not (Chordette). So lets also
-            // match on some other class bits.
-            switch (getDeviceClass()) {
-                case Device.AUDIO_VIDEO_HIFI_AUDIO:
-                case Device.AUDIO_VIDEO_HEADPHONES:
-                case Device.AUDIO_VIDEO_LOUDSPEAKER:
-                case Device.AUDIO_VIDEO_CAR_AUDIO:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_A2DP_SINK) {
-            if (hasService(Service.CAPTURE)) {
-                return true;
-            }
-            // By the A2DP spec, srcs must indicate the CAPTURE service.
-            // However if some device that do not, we try to
-            // match on some other class bits.
-            switch (getDeviceClass()) {
-                case Device.AUDIO_VIDEO_HIFI_AUDIO:
-                case Device.AUDIO_VIDEO_SET_TOP_BOX:
-                case Device.AUDIO_VIDEO_VCR:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_HEADSET) {
-            // The render service class is required by the spec for HFP, so is a
-            // pretty good signal
-            if (hasService(Service.RENDER)) {
-                return true;
-            }
-            // Just in case they forgot the render service class
-            switch (getDeviceClass()) {
-                case Device.AUDIO_VIDEO_HANDSFREE:
-                case Device.AUDIO_VIDEO_WEARABLE_HEADSET:
-                case Device.AUDIO_VIDEO_CAR_AUDIO:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_OPP) {
-            if (hasService(Service.OBJECT_TRANSFER)) {
-                return true;
-            }
-
-            switch (getDeviceClass()) {
-                case Device.COMPUTER_UNCATEGORIZED:
-                case Device.COMPUTER_DESKTOP:
-                case Device.COMPUTER_SERVER:
-                case Device.COMPUTER_LAPTOP:
-                case Device.COMPUTER_HANDHELD_PC_PDA:
-                case Device.COMPUTER_PALM_SIZE_PC_PDA:
-                case Device.COMPUTER_WEARABLE:
-                case Device.PHONE_UNCATEGORIZED:
-                case Device.PHONE_CELLULAR:
-                case Device.PHONE_CORDLESS:
-                case Device.PHONE_SMART:
-                case Device.PHONE_MODEM_OR_GATEWAY:
-                case Device.PHONE_ISDN:
-                    return true;
-                default:
-                    return false;
-            }
-        } else if (profile == PROFILE_HID) {
-            return getMajorDeviceClass() == Device.Major.PERIPHERAL;
-        } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) {
-            // No good way to distinguish between the two, based on class bits.
-            if (hasService(Service.NETWORKING)) {
-                return true;
-            }
-            return getMajorDeviceClass() == Device.Major.NETWORKING;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
deleted file mode 100644
index 9a4151a..0000000
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ /dev/null
@@ -1,807 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Represents the codec configuration for a Bluetooth A2DP source device.
- * <p>Contains the source codec type, the codec priority, the codec sample
- * rate, the codec bits per sample, and the codec channel mode.
- * <p>The source codec type values are the same as those supported by the
- * device hardware.
- *
- * {@see BluetoothA2dp}
- */
-public final class BluetoothCodecConfig implements Parcelable {
-    /** @hide */
-    @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
-            SOURCE_CODEC_TYPE_SBC,
-            SOURCE_CODEC_TYPE_AAC,
-            SOURCE_CODEC_TYPE_APTX,
-            SOURCE_CODEC_TYPE_APTX_HD,
-            SOURCE_CODEC_TYPE_LDAC,
-            SOURCE_CODEC_TYPE_INVALID
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SourceCodecType {}
-
-    /**
-     * Source codec type SBC. This is the mandatory source codec
-     * type.
-     */
-    public static final int SOURCE_CODEC_TYPE_SBC = 0;
-
-    /**
-     * Source codec type AAC.
-     */
-    public static final int SOURCE_CODEC_TYPE_AAC = 1;
-
-    /**
-     * Source codec type APTX.
-     */
-    public static final int SOURCE_CODEC_TYPE_APTX = 2;
-
-    /**
-     * Source codec type APTX HD.
-     */
-    public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
-
-    /**
-     * Source codec type LDAC.
-     */
-    public static final int SOURCE_CODEC_TYPE_LDAC = 4;
-
-    /**
-     * Source codec type invalid. This is the default value used for codec
-     * type.
-     */
-    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
-
-    /**
-     * Represents the count of valid source codec types. Can be accessed via
-     * {@link #getMaxCodecType}.
-     */
-    private static final int SOURCE_CODEC_TYPE_MAX = 5;
-
-    /** @hide */
-    @IntDef(prefix = "CODEC_PRIORITY_", value = {
-            CODEC_PRIORITY_DISABLED,
-            CODEC_PRIORITY_DEFAULT,
-            CODEC_PRIORITY_HIGHEST
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CodecPriority {}
-
-    /**
-     * Codec priority disabled.
-     * Used to indicate that this codec is disabled and should not be used.
-     */
-    public static final int CODEC_PRIORITY_DISABLED = -1;
-
-    /**
-     * Codec priority default.
-     * Default value used for codec priority.
-     */
-    public static final int CODEC_PRIORITY_DEFAULT = 0;
-
-    /**
-     * Codec priority highest.
-     * Used to indicate the highest priority a codec can have.
-     */
-    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
-
-    /** @hide */
-    @IntDef(prefix = "SAMPLE_RATE_", value = {
-            SAMPLE_RATE_NONE,
-            SAMPLE_RATE_44100,
-            SAMPLE_RATE_48000,
-            SAMPLE_RATE_88200,
-            SAMPLE_RATE_96000,
-            SAMPLE_RATE_176400,
-            SAMPLE_RATE_192000
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SampleRate {}
-
-    /**
-     * Codec sample rate 0 Hz. Default value used for
-     * codec sample rate.
-     */
-    public static final int SAMPLE_RATE_NONE = 0;
-
-    /**
-     * Codec sample rate 44100 Hz.
-     */
-    public static final int SAMPLE_RATE_44100 = 0x1 << 0;
-
-    /**
-     * Codec sample rate 48000 Hz.
-     */
-    public static final int SAMPLE_RATE_48000 = 0x1 << 1;
-
-    /**
-     * Codec sample rate 88200 Hz.
-     */
-    public static final int SAMPLE_RATE_88200 = 0x1 << 2;
-
-    /**
-     * Codec sample rate 96000 Hz.
-     */
-    public static final int SAMPLE_RATE_96000 = 0x1 << 3;
-
-    /**
-     * Codec sample rate 176400 Hz.
-     */
-    public static final int SAMPLE_RATE_176400 = 0x1 << 4;
-
-    /**
-     * Codec sample rate 192000 Hz.
-     */
-    public static final int SAMPLE_RATE_192000 = 0x1 << 5;
-
-    /** @hide */
-    @IntDef(prefix = "BITS_PER_SAMPLE_", value = {
-            BITS_PER_SAMPLE_NONE,
-            BITS_PER_SAMPLE_16,
-            BITS_PER_SAMPLE_24,
-            BITS_PER_SAMPLE_32
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BitsPerSample {}
-
-    /**
-     * Codec bits per sample 0. Default value of the codec
-     * bits per sample.
-     */
-    public static final int BITS_PER_SAMPLE_NONE = 0;
-
-    /**
-     * Codec bits per sample 16.
-     */
-    public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
-
-    /**
-     * Codec bits per sample 24.
-     */
-    public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
-
-    /**
-     * Codec bits per sample 32.
-     */
-    public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
-
-    /** @hide */
-    @IntDef(prefix = "CHANNEL_MODE_", value = {
-            CHANNEL_MODE_NONE,
-            CHANNEL_MODE_MONO,
-            CHANNEL_MODE_STEREO
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ChannelMode {}
-
-    /**
-     * Codec channel mode NONE. Default value of the
-     * codec channel mode.
-     */
-    public static final int CHANNEL_MODE_NONE = 0;
-
-    /**
-     * Codec channel mode MONO.
-     */
-    public static final int CHANNEL_MODE_MONO = 0x1 << 0;
-
-    /**
-     * Codec channel mode STEREO.
-     */
-    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
-
-    private final @SourceCodecType int mCodecType;
-    private @CodecPriority int mCodecPriority;
-    private final @SampleRate int mSampleRate;
-    private final @BitsPerSample int mBitsPerSample;
-    private final @ChannelMode int mChannelMode;
-    private final long mCodecSpecific1;
-    private final long mCodecSpecific2;
-    private final long mCodecSpecific3;
-    private final long mCodecSpecific4;
-
-    /**
-     * Creates a new BluetoothCodecConfig.
-     *
-     * @param codecType the source codec type
-     * @param codecPriority the priority of this codec
-     * @param sampleRate the codec sample rate
-     * @param bitsPerSample the bits per sample of this codec
-     * @param channelMode the channel mode of this codec
-     * @param codecSpecific1 the specific value 1
-     * @param codecSpecific2 the specific value 2
-     * @param codecSpecific3 the specific value 3
-     * @param codecSpecific4 the specific value 4
-     * values to 0.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority,
-            @SampleRate int sampleRate, @BitsPerSample int bitsPerSample,
-            @ChannelMode int channelMode, long codecSpecific1,
-            long codecSpecific2, long codecSpecific3,
-            long codecSpecific4) {
-        mCodecType = codecType;
-        mCodecPriority = codecPriority;
-        mSampleRate = sampleRate;
-        mBitsPerSample = bitsPerSample;
-        mChannelMode = channelMode;
-        mCodecSpecific1 = codecSpecific1;
-        mCodecSpecific2 = codecSpecific2;
-        mCodecSpecific3 = codecSpecific3;
-        mCodecSpecific4 = codecSpecific4;
-    }
-
-    /**
-     * Creates a new BluetoothCodecConfig.
-     * <p> By default, the codec priority will be set
-     * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to
-     * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to
-     * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to
-     * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific
-     * values to 0.
-     *
-     * @param codecType the source codec type
-     */
-    public BluetoothCodecConfig(@SourceCodecType int codecType) {
-        this(codecType, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                BluetoothCodecConfig.SAMPLE_RATE_NONE,
-                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
-                BluetoothCodecConfig.CHANNEL_MODE_NONE, 0, 0, 0, 0);
-    }
-
-    private BluetoothCodecConfig(Parcel in) {
-        mCodecType = in.readInt();
-        mCodecPriority = in.readInt();
-        mSampleRate = in.readInt();
-        mBitsPerSample = in.readInt();
-        mChannelMode = in.readInt();
-        mCodecSpecific1 = in.readLong();
-        mCodecSpecific2 = in.readLong();
-        mCodecSpecific3 = in.readLong();
-        mCodecSpecific4 = in.readLong();
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothCodecConfig) {
-            BluetoothCodecConfig other = (BluetoothCodecConfig) o;
-            return (other.mCodecType == mCodecType
-                    && other.mCodecPriority == mCodecPriority
-                    && other.mSampleRate == mSampleRate
-                    && other.mBitsPerSample == mBitsPerSample
-                    && other.mChannelMode == mChannelMode
-                    && other.mCodecSpecific1 == mCodecSpecific1
-                    && other.mCodecSpecific2 == mCodecSpecific2
-                    && other.mCodecSpecific3 == mCodecSpecific3
-                    && other.mCodecSpecific4 == mCodecSpecific4);
-        }
-        return false;
-    }
-
-    /**
-     * Returns a hash representation of this BluetoothCodecConfig
-     * based on all the config values.
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
-                mBitsPerSample, mChannelMode, mCodecSpecific1,
-                mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
-    }
-
-    /**
-     * Adds capability string to an existing string.
-     *
-     * @param prevStr the previous string with the capabilities. Can be a {@code null} pointer
-     * @param capStr the capability string to append to prevStr argument
-     * @return the result string in the form "prevStr|capStr"
-     */
-    private static String appendCapabilityToString(@Nullable String prevStr,
-            @NonNull String capStr) {
-        if (prevStr == null) {
-            return capStr;
-        }
-        return prevStr + "|" + capStr;
-    }
-
-    /**
-     * Returns a {@link String} that describes each BluetoothCodecConfig parameter
-     * current value.
-     */
-    @Override
-    public String toString() {
-        String sampleRateStr = null;
-        if (mSampleRate == SAMPLE_RATE_NONE) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE");
-        }
-        if ((mSampleRate & SAMPLE_RATE_44100) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "44100");
-        }
-        if ((mSampleRate & SAMPLE_RATE_48000) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "48000");
-        }
-        if ((mSampleRate & SAMPLE_RATE_88200) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "88200");
-        }
-        if ((mSampleRate & SAMPLE_RATE_96000) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "96000");
-        }
-        if ((mSampleRate & SAMPLE_RATE_176400) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "176400");
-        }
-        if ((mSampleRate & SAMPLE_RATE_192000) != 0) {
-            sampleRateStr = appendCapabilityToString(sampleRateStr, "192000");
-        }
-
-        String bitsPerSampleStr = null;
-        if (mBitsPerSample == BITS_PER_SAMPLE_NONE) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE");
-        }
-        if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16");
-        }
-        if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24");
-        }
-        if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) {
-            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32");
-        }
-
-        String channelModeStr = null;
-        if (mChannelMode == CHANNEL_MODE_NONE) {
-            channelModeStr = appendCapabilityToString(channelModeStr, "NONE");
-        }
-        if ((mChannelMode & CHANNEL_MODE_MONO) != 0) {
-            channelModeStr = appendCapabilityToString(channelModeStr, "MONO");
-        }
-        if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) {
-            channelModeStr = appendCapabilityToString(channelModeStr, "STEREO");
-        }
-
-        return "{codecName:" + getCodecName()
-                + ",mCodecType:" + mCodecType
-                + ",mCodecPriority:" + mCodecPriority
-                + ",mSampleRate:" + String.format("0x%x", mSampleRate)
-                + "(" + sampleRateStr + ")"
-                + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample)
-                + "(" + bitsPerSampleStr + ")"
-                + ",mChannelMode:" + String.format("0x%x", mChannelMode)
-                + "(" + channelModeStr + ")"
-                + ",mCodecSpecific1:" + mCodecSpecific1
-                + ",mCodecSpecific2:" + mCodecSpecific2
-                + ",mCodecSpecific3:" + mCodecSpecific3
-                + ",mCodecSpecific4:" + mCodecSpecific4 + "}";
-    }
-
-    /**
-     * @return 0
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecConfig> CREATOR =
-            new Parcelable.Creator<BluetoothCodecConfig>() {
-                public BluetoothCodecConfig createFromParcel(Parcel in) {
-                    return new BluetoothCodecConfig(in);
-                }
-
-                public BluetoothCodecConfig[] newArray(int size) {
-                    return new BluetoothCodecConfig[size];
-                }
-            };
-
-    /**
-     * Flattens the object to a parcel
-     *
-     * @param out The Parcel in which the object should be written
-     * @param flags Additional flags about how the object should be written
-     *
-     * @hide
-     */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mCodecType);
-        out.writeInt(mCodecPriority);
-        out.writeInt(mSampleRate);
-        out.writeInt(mBitsPerSample);
-        out.writeInt(mChannelMode);
-        out.writeLong(mCodecSpecific1);
-        out.writeLong(mCodecSpecific2);
-        out.writeLong(mCodecSpecific3);
-        out.writeLong(mCodecSpecific4);
-    }
-
-    /**
-     * Returns the codec name converted to {@link String}.
-     * @hide
-     */
-    public @NonNull String getCodecName() {
-        switch (mCodecType) {
-            case SOURCE_CODEC_TYPE_SBC:
-                return "SBC";
-            case SOURCE_CODEC_TYPE_AAC:
-                return "AAC";
-            case SOURCE_CODEC_TYPE_APTX:
-                return "aptX";
-            case SOURCE_CODEC_TYPE_APTX_HD:
-                return "aptX HD";
-            case SOURCE_CODEC_TYPE_LDAC:
-                return "LDAC";
-            case SOURCE_CODEC_TYPE_INVALID:
-                return "INVALID CODEC";
-            default:
-                break;
-        }
-        return "UNKNOWN CODEC(" + mCodecType + ")";
-    }
-
-    /**
-     * Returns the source codec type of this config.
-     */
-    public @SourceCodecType int getCodecType() {
-        return mCodecType;
-    }
-
-    /**
-     * Returns the valid codec types count.
-     */
-    public static int getMaxCodecType() {
-        return SOURCE_CODEC_TYPE_MAX;
-    }
-
-    /**
-     * Checks whether the codec is mandatory.
-     * <p> The actual mandatory codec type for Android Bluetooth audio is SBC.
-     * See {@link #SOURCE_CODEC_TYPE_SBC}.
-     *
-     * @return {@code true} if the codec is mandatory, {@code false} otherwise
-     * @hide
-     */
-    public boolean isMandatoryCodec() {
-        return mCodecType == SOURCE_CODEC_TYPE_SBC;
-    }
-
-    /**
-     * Returns the codec selection priority.
-     * <p>The codec selection priority is relative to other codecs: larger value
-     * means higher priority.
-     */
-    public @CodecPriority int getCodecPriority() {
-        return mCodecPriority;
-    }
-
-    /**
-     * Sets the codec selection priority.
-     * <p>The codec selection priority is relative to other codecs: larger value
-     * means higher priority.
-     *
-     * @param codecPriority the priority this codec should have
-     * @hide
-     */
-    public void setCodecPriority(@CodecPriority int codecPriority) {
-        mCodecPriority = codecPriority;
-    }
-
-    /**
-     * Returns the codec sample rate. The value can be a bitmask with all
-     * supported sample rates.
-     */
-    public @SampleRate int getSampleRate() {
-        return mSampleRate;
-    }
-
-    /**
-     * Returns the codec bits per sample. The value can be a bitmask with all
-     * bits per sample supported.
-     */
-    public @BitsPerSample int getBitsPerSample() {
-        return mBitsPerSample;
-    }
-
-    /**
-     * Returns the codec channel mode. The value can be a bitmask with all
-     * supported channel modes.
-     */
-    public @ChannelMode int getChannelMode() {
-        return mChannelMode;
-    }
-
-    /**
-     * Returns the codec specific value1.
-     */
-    public long getCodecSpecific1() {
-        return mCodecSpecific1;
-    }
-
-    /**
-     * Returns the codec specific value2.
-     */
-    public long getCodecSpecific2() {
-        return mCodecSpecific2;
-    }
-
-    /**
-     * Returns the codec specific value3.
-     */
-    public long getCodecSpecific3() {
-        return mCodecSpecific3;
-    }
-
-    /**
-     * Returns the codec specific value4.
-     */
-    public long getCodecSpecific4() {
-        return mCodecSpecific4;
-    }
-
-    /**
-     * Checks whether a value set presented by a bitmask has zero or single bit
-     *
-     * @param valueSet the value set presented by a bitmask
-     * @return {@code true} if the valueSet contains zero or single bit, {@code false} otherwise
-     * @hide
-     */
-    private static boolean hasSingleBit(int valueSet) {
-        return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
-    }
-
-    /**
-     * Returns whether the object contains none or single sample rate.
-     * @hide
-     */
-    public boolean hasSingleSampleRate() {
-        return hasSingleBit(mSampleRate);
-    }
-
-    /**
-     * Returns whether the object contains none or single bits per sample.
-     * @hide
-     */
-    public boolean hasSingleBitsPerSample() {
-        return hasSingleBit(mBitsPerSample);
-    }
-
-    /**
-     * Returns whether the object contains none or single channel mode.
-     * @hide
-     */
-    public boolean hasSingleChannelMode() {
-        return hasSingleBit(mChannelMode);
-    }
-
-    /**
-     * Checks whether the audio feeding parameters are the same.
-     *
-     * @param other the codec config to compare against
-     * @return {@code true} if the audio feeding parameters are same, {@code false} otherwise
-     * @hide
-     */
-    public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
-        return (other != null && other.mSampleRate == mSampleRate
-                && other.mBitsPerSample == mBitsPerSample
-                && other.mChannelMode == mChannelMode);
-    }
-
-    /**
-     * Checks whether another codec config has the similar feeding parameters.
-     * Any parameters with NONE value will be considered to be a wildcard matching.
-     *
-     * @param other the codec config to compare against
-     * @return {@code true} if the audio feeding parameters are similar, {@code false} otherwise
-     * @hide
-     */
-    public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
-        if (other == null || mCodecType != other.mCodecType) {
-            return false;
-        }
-        int sampleRate = other.mSampleRate;
-        if (mSampleRate == SAMPLE_RATE_NONE
-                || sampleRate == SAMPLE_RATE_NONE) {
-            sampleRate = mSampleRate;
-        }
-        int bitsPerSample = other.mBitsPerSample;
-        if (mBitsPerSample == BITS_PER_SAMPLE_NONE
-                || bitsPerSample == BITS_PER_SAMPLE_NONE) {
-            bitsPerSample = mBitsPerSample;
-        }
-        int channelMode = other.mChannelMode;
-        if (mChannelMode == CHANNEL_MODE_NONE
-                || channelMode == CHANNEL_MODE_NONE) {
-            channelMode = mChannelMode;
-        }
-        return sameAudioFeedingParameters(new BluetoothCodecConfig(
-                mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode,
-                /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0,
-                /* specific4 */ 0));
-    }
-
-    /**
-     * Checks whether the codec specific parameters are the same.
-     * <p> Currently, only AAC VBR and LDAC Playback Quality on CodecSpecific1
-     * are compared.
-     *
-     * @param other the codec config to compare against
-     * @return {@code true} if the codec specific parameters are the same, {@code false} otherwise
-     * @hide
-     */
-    public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
-        if (other == null && mCodecType != other.mCodecType) {
-            return false;
-        }
-        switch (mCodecType) {
-            case SOURCE_CODEC_TYPE_AAC:
-            case SOURCE_CODEC_TYPE_LDAC:
-                if (mCodecSpecific1 != other.mCodecSpecific1) {
-                    return false;
-                }
-            default:
-                return true;
-        }
-    }
-
-    /**
-     * Builder for {@link BluetoothCodecConfig}.
-     * <p> By default, the codec type will be set to
-     * {@link BluetoothCodecConfig#SOURCE_CODEC_TYPE_INVALID}, the codec priority
-     * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to
-     * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to
-     * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to
-     * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific
-     * values to 0.
-     */
-    public static final class Builder {
-        private int mCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
-        private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
-        private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE;
-        private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
-        private int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE;
-        private long mCodecSpecific1 = 0;
-        private long mCodecSpecific2 = 0;
-        private long mCodecSpecific3 = 0;
-        private long mCodecSpecific4 = 0;
-
-        /**
-         * Set codec type for Bluetooth codec config.
-         *
-         * @param codecType of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
-            mCodecType = codecType;
-            return this;
-        }
-
-        /**
-         * Set codec priority for Bluetooth codec config.
-         *
-         * @param codecPriority of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) {
-            mCodecPriority = codecPriority;
-            return this;
-        }
-
-        /**
-         * Set sample rate for Bluetooth codec config.
-         *
-         * @param sampleRate of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setSampleRate(@SampleRate int sampleRate) {
-            mSampleRate = sampleRate;
-            return this;
-        }
-
-        /**
-         * Set the bits per sample for Bluetooth codec config.
-         *
-         * @param bitsPerSample of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) {
-            mBitsPerSample = bitsPerSample;
-            return this;
-        }
-
-        /**
-         * Set the channel mode for Bluetooth codec config.
-         *
-         * @param channelMode of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setChannelMode(@ChannelMode int channelMode) {
-            mChannelMode = channelMode;
-            return this;
-        }
-
-        /**
-         * Set the first codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific1 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific1(long codecSpecific1) {
-            mCodecSpecific1 = codecSpecific1;
-            return this;
-        }
-
-        /**
-         * Set the second codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific2 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific2(long codecSpecific2) {
-            mCodecSpecific2 = codecSpecific2;
-            return this;
-        }
-
-        /**
-         * Set the third codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific3 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific3(long codecSpecific3) {
-            mCodecSpecific3 = codecSpecific3;
-            return this;
-        }
-
-        /**
-         * Set the fourth codec specific values for Bluetooth codec config.
-         *
-         * @param codecSpecific4 codec specific value or 0 if default
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecSpecific4(long codecSpecific4) {
-            mCodecSpecific4 = codecSpecific4;
-            return this;
-        }
-
-        /**
-         * Build {@link BluetoothCodecConfig}.
-         * @return new BluetoothCodecConfig built
-         */
-        public @NonNull BluetoothCodecConfig build() {
-            return new BluetoothCodecConfig(mCodecType, mCodecPriority,
-                    mSampleRate, mBitsPerSample,
-                    mChannelMode, mCodecSpecific1,
-                    mCodecSpecific2, mCodecSpecific3,
-                    mCodecSpecific4);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
deleted file mode 100644
index 02606fe..0000000
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Represents the codec status (configuration and capability) for a Bluetooth
- * A2DP source device.
- *
- * {@see BluetoothA2dp}
- */
-public final class BluetoothCodecStatus implements Parcelable {
-    /**
-     * Extra for the codec configuration intents of the individual profiles.
-     *
-     * This extra represents the current codec status of the A2DP
-     * profile.
-     */
-    public static final String EXTRA_CODEC_STATUS =
-            "android.bluetooth.extra.CODEC_STATUS";
-
-    private final @Nullable BluetoothCodecConfig mCodecConfig;
-    private final @Nullable List<BluetoothCodecConfig> mCodecsLocalCapabilities;
-    private final @Nullable List<BluetoothCodecConfig> mCodecsSelectableCapabilities;
-
-    public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig,
-            @Nullable List<BluetoothCodecConfig> codecsLocalCapabilities,
-            @Nullable List<BluetoothCodecConfig> codecsSelectableCapabilities) {
-        mCodecConfig = codecConfig;
-        mCodecsLocalCapabilities = codecsLocalCapabilities;
-        mCodecsSelectableCapabilities = codecsSelectableCapabilities;
-    }
-
-    private BluetoothCodecStatus(Parcel in) {
-        mCodecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR);
-        mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR);
-        mCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothCodecStatus) {
-            BluetoothCodecStatus other = (BluetoothCodecStatus) o;
-            return (Objects.equals(other.mCodecConfig, mCodecConfig)
-                    && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
-                    && sameCapabilities(other.mCodecsSelectableCapabilities,
-                    mCodecsSelectableCapabilities));
-        }
-        return false;
-    }
-
-    /**
-     * Checks whether two lists of capabilities contain same capabilities.
-     * The order of the capabilities in each list is ignored.
-     *
-     * @param c1 the first list of capabilities to compare
-     * @param c2 the second list of capabilities to compare
-     * @return {@code true} if both lists contain same capabilities
-     */
-    private static boolean sameCapabilities(@Nullable List<BluetoothCodecConfig> c1,
-                                           @Nullable List<BluetoothCodecConfig> c2) {
-        if (c1 == null) {
-            return (c2 == null);
-        }
-        if (c2 == null) {
-            return false;
-        }
-        if (c1.size() != c2.size()) {
-            return false;
-        }
-        return c1.containsAll(c2);
-    }
-
-    /**
-     * Checks whether the codec config matches the selectable capabilities.
-     * Any parameters of the codec config with NONE value will be considered a wildcard matching.
-     *
-     * @param codecConfig the codec config to compare against
-     * @return {@code true} if the codec config matches, {@code false} otherwise
-     */
-    public boolean isCodecConfigSelectable(@Nullable BluetoothCodecConfig codecConfig) {
-        if (codecConfig == null || !codecConfig.hasSingleSampleRate()
-                || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) {
-            return false;
-        }
-        for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
-            if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
-                continue;
-            }
-            int sampleRate = codecConfig.getSampleRate();
-            if ((sampleRate & selectableConfig.getSampleRate()) == 0
-                    && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
-                continue;
-            }
-            int bitsPerSample = codecConfig.getBitsPerSample();
-            if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
-                    && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
-                continue;
-            }
-            int channelMode = codecConfig.getChannelMode();
-            if ((channelMode & selectableConfig.getChannelMode()) == 0
-                    && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
-                continue;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns a hash based on the codec config and local capabilities.
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
-                mCodecsLocalCapabilities);
-    }
-
-    /**
-     * Returns a {@link String} that describes each BluetoothCodecStatus parameter
-     * current value.
-     */
-    @Override
-    public String toString() {
-        return "{mCodecConfig:" + mCodecConfig
-                + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities
-                + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities
-                + "}";
-    }
-
-    /**
-     * @return 0
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecStatus> CREATOR =
-            new Parcelable.Creator<BluetoothCodecStatus>() {
-                public BluetoothCodecStatus createFromParcel(Parcel in) {
-                    return new BluetoothCodecStatus(in);
-                }
-
-                public BluetoothCodecStatus[] newArray(int size) {
-                    return new BluetoothCodecStatus[size];
-                }
-            };
-
-    /**
-     * Flattens the object to a parcel.
-     *
-     * @param out The Parcel in which the object should be written
-     * @param flags Additional flags about how the object should be written
-     */
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeTypedObject(mCodecConfig, 0);
-        out.writeTypedList(mCodecsLocalCapabilities);
-        out.writeTypedList(mCodecsSelectableCapabilities);
-    }
-
-    /**
-     * Returns the current codec configuration.
-     */
-    public @Nullable BluetoothCodecConfig getCodecConfig() {
-        return mCodecConfig;
-    }
-
-    /**
-     * Returns the codecs local capabilities.
-     */
-    public @NonNull List<BluetoothCodecConfig> getCodecsLocalCapabilities() {
-        return (mCodecsLocalCapabilities == null)
-                ? Collections.emptyList() : mCodecsLocalCapabilities;
-    }
-
-    /**
-     * Returns the codecs selectable capabilities.
-     */
-    public @NonNull List<BluetoothCodecConfig> getCodecsSelectableCapabilities() {
-        return (mCodecsSelectableCapabilities == null)
-                ? Collections.emptyList() : mCodecsSelectableCapabilities;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
deleted file mode 100644
index ba57ec4..0000000
--- a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth CSIP set coordinator.
- *
- * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothCsipSetCoordinator proxy object.
- *
- */
-public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable {
-    private static final String TAG = "BluetoothCsipSetCoordinator";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /**
-     * @hide
-     */
-    @SystemApi
-    public interface ClientLockCallback {
-        /**
-         * @hide
-         */
-        @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked);
-    }
-
-    private static class BluetoothCsipSetCoordinatorLockCallbackDelegate
-            extends IBluetoothCsipSetCoordinatorLockCallback.Stub {
-        private final ClientLockCallback mCallback;
-        private final Executor mExecutor;
-
-        BluetoothCsipSetCoordinatorLockCallbackDelegate(
-                Executor executor, ClientLockCallback callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) {
-            mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked));
-        }
-    };
-
-    /**
-     * Intent used to broadcast the change in connection state of the CSIS
-     * Client.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to expose broadcast receiving device.
-     *
-     * <p>This intent will have 2 extras:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device. </li>
-     * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
-     * <li> {@link #EXTRA_CSIS_GROUP_SIZE} - Group size. </li>
-     * <li> {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CSIS_DEVICE_AVAILABLE =
-            "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
-     * Contains the group id.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID";
-
-    /**
-     * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE";
-
-    /**
-     * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CSIS_GROUP_TYPE_UUID =
-            "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID";
-
-    /**
-     * Intent used to broadcast information about identified set member
-     * ready to connect.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * <li>  {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE =
-            "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
-
-    /**
-     * This represents an invalid group ID.
-     *
-     * @hide
-     */
-    public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID;
-
-    /**
-     * Indicating that group was locked with success.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_SUCCESS = 0;
-
-    /**
-     * Indicating that group locked failed due to invalid group ID.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1;
-
-    /**
-     * Indicating that group locked failed due to empty group.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2;
-
-    /**
-     * Indicating that group locked failed due to group members being disconnected.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3;
-
-    /**
-     * Indicating that group locked failed due to group member being already locked.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4;
-
-    /**
-     * Indicating that group locked failed due to other reason.
-     *
-     * @hide
-     */
-    public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5;
-
-    /**
-     * Indicating that group member in locked state was lost.
-     *
-     * @hide
-     */
-    public static final int LOCKED_GROUP_MEMBER_LOST = 6;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG,
-                    IBluetoothCsipSetCoordinator.class.getName()) {
-                @Override
-                public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
-                    return IBluetoothCsipSetCoordinator.Stub.asInterface(service);
-                }
-            };
-
-    /**
-     * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local
-     * Bluetooth CSIS service.
-     */
-    /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    /**
-     * @hide
-     */
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    /**
-     * @hide
-     */
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothCsipSetCoordinator getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Lock the set.
-     * @param groupId group ID to lock,
-     * @param executor callback executor,
-     * @param cb callback to report lock and unlock events - stays valid until the app unlocks
-     *           using the returned lock identifier or the lock timeouts on the remote side,
-     *           as per CSIS specification,
-     * @return unique lock identifier used for unlocking or null if lock has failed.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public
-    @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
-            @Nullable ClientLockCallback cb) {
-        if (VDBG) log("groupLockSet()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final UUID defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            IBluetoothCsipSetCoordinatorLockCallback delegate = null;
-            if ((executor != null) && (cb != null)) {
-                delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
-            }
-            try {
-                final SynchronousResultReceiver<ParcelUuid> recv = new SynchronousResultReceiver();
-                service.groupLock(groupId, delegate, mAttributionSource, recv);
-                final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-                return ret == null ? defaultValue : ret.getUuid();
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Unlock the set.
-     * @param lockUuid unique lock identifier
-     * @return true if unlocked, false on error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean groupUnlock(@NonNull UUID lockUuid) {
-        if (VDBG) log("groupLockSet()");
-        if (lockUuid == null) {
-            return false;
-        }
-        final IBluetoothCsipSetCoordinator service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-                return true;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get device's groups.
-     * @param device the active device
-     * @return Map of groups ids and related UUIDs
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getGroupUuidMapByDevice()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final Map defaultValue = new HashMap<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Map> recv = new SynchronousResultReceiver();
-                service.getGroupUuidMapByDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get group id for the given UUID
-     * @param uuid
-     * @return list of group IDs
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
-        if (VDBG) log("getAllGroupIds()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final List<Integer> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<Integer>> recv =
-                        new SynchronousResultReceiver();
-                service.getAllGroupIds(uuid, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public
-    @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) {
-        if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public
-    @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean setConnectionPolicy(
-            @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothCsipSetCoordinator service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
deleted file mode 100644
index fc99942..0000000
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ /dev/null
@@ -1,2830 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.companion.AssociationRequest;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.UUID;
-
-/**
- * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
- * create a connection with the respective device or query information about
- * it, such as the name, address, class, and bonding state.
- *
- * <p>This class is really just a thin wrapper for a Bluetooth hardware
- * address. Objects of this class are immutable. Operations on this class
- * are performed on the remote Bluetooth hardware address, using the
- * {@link BluetoothAdapter} that was used to create this {@link
- * BluetoothDevice}.
- *
- * <p>To get a {@link BluetoothDevice}, use
- * {@link BluetoothAdapter#getRemoteDevice(String)
- * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device
- * of a known MAC address (which you can get through device discovery with
- * {@link BluetoothAdapter}) or get one from the set of bonded devices
- * returned by {@link BluetoothAdapter#getBondedDevices()
- * BluetoothAdapter.getBondedDevices()}. You can then open a
- * {@link BluetoothSocket} for communication with the remote device, using
- * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
- * {@link #createL2capChannel(int)} over Bluetooth LE.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about using Bluetooth, read the <a href=
- * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
- * guide.
- * </p>
- * </div>
- *
- * {@see BluetoothAdapter}
- * {@see BluetoothSocket}
- */
-public final class BluetoothDevice implements Parcelable, Attributable {
-    private static final String TAG = "BluetoothDevice";
-    private static final boolean DBG = false;
-
-    /**
-     * Connection state bitmask as returned by getConnectionState.
-     */
-    private static final int CONNECTION_STATE_DISCONNECTED = 0;
-    private static final int CONNECTION_STATE_CONNECTED = 1;
-    private static final int CONNECTION_STATE_ENCRYPTED_BREDR = 2;
-    private static final int CONNECTION_STATE_ENCRYPTED_LE = 4;
-
-    /**
-     * Sentinel error value for this class. Guaranteed to not equal any other
-     * integer constant in this class. Provided as a convenience for functions
-     * that require a sentinel error value, for example:
-     * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
-     * BluetoothDevice.ERROR)</code>
-     */
-    public static final int ERROR = Integer.MIN_VALUE;
-
-    /**
-     * Broadcast Action: Remote device discovered.
-     * <p>Sent when a remote device is found during discovery.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
-     * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available.
-     */
-    // TODO: Change API to not broadcast RSSI if not available (incoming connection)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_FOUND =
-            "android.bluetooth.device.action.FOUND";
-
-    /**
-     * Broadcast Action: Bluetooth class of a remote device has changed.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_CLASS}.
-     * {@see BluetoothClass}
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CLASS_CHANGED =
-            "android.bluetooth.device.action.CLASS_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates a low level (ACL) connection has been
-     * established with a remote device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     * <p>ACL connections are managed automatically by the Android Bluetooth
-     * stack.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACL_CONNECTED =
-            "android.bluetooth.device.action.ACL_CONNECTED";
-
-    /**
-     * Broadcast Action: Indicates that a low level (ACL) disconnection has
-     * been requested for a remote device, and it will soon be disconnected.
-     * <p>This is useful for graceful disconnection. Applications should use
-     * this intent as a hint to immediately terminate higher level connections
-     * (RFCOMM, L2CAP, or profile connections) to the remote device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACL_DISCONNECT_REQUESTED =
-            "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
-
-    /**
-     * Broadcast Action: Indicates a low level (ACL) disconnection from a
-     * remote device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     * <p>ACL connections are managed automatically by the Android Bluetooth
-     * stack.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACL_DISCONNECTED =
-            "android.bluetooth.device.action.ACL_DISCONNECTED";
-
-    /**
-     * Broadcast Action: Indicates the friendly name of a remote device has
-     * been retrieved for the first time, or changed since the last retrieval.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_NAME}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NAME_CHANGED =
-            "android.bluetooth.device.action.NAME_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates the alias of a remote device has been
-     * changed.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     */
-    @SuppressLint("ActionValue")
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ALIAS_CHANGED =
-            "android.bluetooth.device.action.ALIAS_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates a change in the bond state of a remote
-     * device. For example, if a device is bonded (paired).
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
-     * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
-     */
-    // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
-    // contain a hidden extra field EXTRA_REASON with the result code.
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BOND_STATE_CHANGED =
-            "android.bluetooth.device.action.BOND_STATE_CHANGED";
-
-    /**
-     * Broadcast Action: Indicates the battery level of a remote device has
-     * been retrieved for the first time, or changed since the last retrieval
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
-     * #EXTRA_BATTERY_LEVEL}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BATTERY_LEVEL_CHANGED =
-            "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED";
-
-    /**
-     * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED}
-     * intent. It contains the most recently retrieved battery level information
-     * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN}
-     * when the valid is unknown or there is an error
-     *
-     * @hide
-     */
-    public static final String EXTRA_BATTERY_LEVEL =
-            "android.bluetooth.device.extra.BATTERY_LEVEL";
-
-    /**
-     * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()}
-     *
-     * @hide
-     */
-    public static final int BATTERY_LEVEL_UNKNOWN = -1;
-
-    /**
-     * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off
-     *
-     * @hide
-     */
-    public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100;
-
-    /**
-     * Used as a Parcelable {@link BluetoothDevice} extra field in every intent
-     * broadcast by this class. It contains the {@link BluetoothDevice} that
-     * the intent applies to.
-     */
-    public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
-
-    /**
-     * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link
-     * #ACTION_FOUND} intents. It contains the friendly Bluetooth name.
-     */
-    public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
-
-    /**
-     * Used as an optional short extra field in {@link #ACTION_FOUND} intents.
-     * Contains the RSSI value of the remote device as reported by the
-     * Bluetooth hardware.
-     */
-    public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
-
-    /**
-    * Used as an bool extra field in {@link #ACTION_FOUND} intents.
-    * It contains the information if device is discovered as member of a coordinated set or not.
-    * Pairing with device that belongs to a set would trigger pairing with the rest of set members.
-    * See Bluetooth CSIP specification for more details.
-    */
-    public static final String EXTRA_IS_COORDINATED_SET_MEMBER =
-            "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER";
-
-    /**
-     * Used as a Parcelable {@link BluetoothClass} extra field in {@link
-     * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
-     */
-    public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
-     * Contains the bond state of the remote device.
-     * <p>Possible values are:
-     * {@link #BOND_NONE},
-     * {@link #BOND_BONDING},
-     * {@link #BOND_BONDED}.
-     */
-    public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
-    /**
-     * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
-     * Contains the previous bond state of the remote device.
-     * <p>Possible values are:
-     * {@link #BOND_NONE},
-     * {@link #BOND_BONDING},
-     * {@link #BOND_BONDED}.
-     */
-    public static final String EXTRA_PREVIOUS_BOND_STATE =
-            "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
-    /**
-     * Indicates the remote device is not bonded (paired).
-     * <p>There is no shared link key with the remote device, so communication
-     * (if it is allowed at all) will be unauthenticated and unencrypted.
-     */
-    public static final int BOND_NONE = 10;
-    /**
-     * Indicates bonding (pairing) is in progress with the remote device.
-     */
-    public static final int BOND_BONDING = 11;
-    /**
-     * Indicates the remote device is bonded (paired).
-     * <p>A shared link keys exists locally for the remote device, so
-     * communication can be authenticated and encrypted.
-     * <p><i>Being bonded (paired) with a remote device does not necessarily
-     * mean the device is currently connected. It just means that the pending
-     * procedure was completed at some earlier time, and the link key is still
-     * stored locally, ready to use on the next connection.
-     * </i>
-     */
-    public static final int BOND_BONDED = 12;
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents for unbond reason.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents to indicate pairing method used. Possible values are:
-     * {@link #PAIRING_VARIANT_PIN},
-     * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION},
-     */
-    public static final String EXTRA_PAIRING_VARIANT =
-            "android.bluetooth.device.extra.PAIRING_VARIANT";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents as the value of passkey.
-     */
-    public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
-     * intents as the value of passkey.
-     * @hide
-     */
-    public static final String EXTRA_PAIRING_INITIATOR =
-            "android.bluetooth.device.extra.PAIRING_INITIATOR";
-
-    /**
-     * Bluetooth pairing initiator, Foreground App
-     * @hide
-     */
-    public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1;
-
-    /**
-     * Bluetooth pairing initiator, Background
-     * @hide
-     */
-    public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2;
-
-    /**
-     * Bluetooth device type, Unknown
-     */
-    public static final int DEVICE_TYPE_UNKNOWN = 0;
-
-    /**
-     * Bluetooth device type, Classic - BR/EDR devices
-     */
-    public static final int DEVICE_TYPE_CLASSIC = 1;
-
-    /**
-     * Bluetooth device type, Low Energy - LE-only
-     */
-    public static final int DEVICE_TYPE_LE = 2;
-
-    /**
-     * Bluetooth device type, Dual Mode - BR/EDR/LE
-     */
-    public static final int DEVICE_TYPE_DUAL = 3;
-
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final String ACTION_SDP_RECORD =
-            "android.bluetooth.device.action.SDP_RECORD";
-
-    /** @hide */
-    @IntDef(prefix = "METADATA_", value = {
-            METADATA_MANUFACTURER_NAME,
-            METADATA_MODEL_NAME,
-            METADATA_SOFTWARE_VERSION,
-            METADATA_HARDWARE_VERSION,
-            METADATA_COMPANION_APP,
-            METADATA_MAIN_ICON,
-            METADATA_IS_UNTETHERED_HEADSET,
-            METADATA_UNTETHERED_LEFT_ICON,
-            METADATA_UNTETHERED_RIGHT_ICON,
-            METADATA_UNTETHERED_CASE_ICON,
-            METADATA_UNTETHERED_LEFT_BATTERY,
-            METADATA_UNTETHERED_RIGHT_BATTERY,
-            METADATA_UNTETHERED_CASE_BATTERY,
-            METADATA_UNTETHERED_LEFT_CHARGING,
-            METADATA_UNTETHERED_RIGHT_CHARGING,
-            METADATA_UNTETHERED_CASE_CHARGING,
-            METADATA_ENHANCED_SETTINGS_UI_URI,
-            METADATA_DEVICE_TYPE,
-            METADATA_MAIN_BATTERY,
-            METADATA_MAIN_CHARGING,
-            METADATA_MAIN_LOW_BATTERY_THRESHOLD,
-            METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
-            METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
-            METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MetadataKey{}
-
-    /**
-     * Maximum length of a metadata entry, this is to avoid exploding Bluetooth
-     * disk usage
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAX_LENGTH = 2048;
-
-    /**
-     * Manufacturer name of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MANUFACTURER_NAME = 0;
-
-    /**
-     * Model name of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MODEL_NAME = 1;
-
-    /**
-     * Software version of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_SOFTWARE_VERSION = 2;
-
-    /**
-     * Hardware version of this Bluetooth device
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_HARDWARE_VERSION = 3;
-
-    /**
-     * Package name of the companion app, if any
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_COMPANION_APP = 4;
-
-    /**
-     * URI to the main icon shown on the settings UI
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_ICON = 5;
-
-    /**
-     * Whether this device is an untethered headset with left, right and case
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_IS_UNTETHERED_HEADSET = 6;
-
-    /**
-     * URI to icon of the left headset
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_ICON = 7;
-
-    /**
-     * URI to icon of the right headset
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_ICON = 8;
-
-    /**
-     * URI to icon of the headset charging case
-     * Data type should be {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_ICON = 9;
-
-    /**
-     * Battery level of left headset
-     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
-     * as invalid.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10;
-
-    /**
-     * Battery level of rigth headset
-     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
-     * as invalid.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11;
-
-    /**
-     * Battery level of the headset charging case
-     * Data type should be {@String} 0-100 as {@link Byte} array, otherwise
-     * as invalid.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_BATTERY = 12;
-
-    /**
-     * Whether the left headset is charging
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13;
-
-    /**
-     * Whether the right headset is charging
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14;
-
-    /**
-     * Whether the headset charging case is charging
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_CHARGING = 15;
-
-    /**
-     * URI to the enhanced settings UI slice
-     * Data type should be {@String} as {@link Byte} array, null means
-     * the UI does not exist.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16;
-
-    /**
-     * Type of the Bluetooth device, must be within the list of
-     * BluetoothDevice.DEVICE_TYPE_*
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_DEVICE_TYPE = 17;
-
-    /**
-     * Battery level of the Bluetooth device, use when the Bluetooth device
-     * does not support HFP battery indicator.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_BATTERY = 18;
-
-    /**
-     * Whether the device is charging.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_CHARGING = 19;
-
-    /**
-     * The battery threshold of the Bluetooth device to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20;
-
-    /**
-     * The battery threshold of the left headset to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21;
-
-    /**
-     * The battery threshold of the right headset to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22;
-
-    /**
-     * The battery threshold of the case to show low battery icon.
-     * Data type should be {@String} as {@link Byte} array.
-     * @hide
-     */
-    @SystemApi
-    public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23;
-
-    /**
-     * Device type which is used in METADATA_DEVICE_TYPE
-     * Indicates this Bluetooth device is a standard Bluetooth accessory or
-     * not listed in METADATA_DEVICE_TYPE_*.
-     * @hide
-     */
-    @SystemApi
-    public static final String DEVICE_TYPE_DEFAULT = "Default";
-
-    /**
-     * Device type which is used in METADATA_DEVICE_TYPE
-     * Indicates this Bluetooth device is a watch.
-     * @hide
-     */
-    @SystemApi
-    public static final String DEVICE_TYPE_WATCH = "Watch";
-
-    /**
-     * Device type which is used in METADATA_DEVICE_TYPE
-     * Indicates this Bluetooth device is an untethered headset.
-     * @hide
-     */
-    @SystemApi
-    public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset";
-
-    /**
-     * Broadcast Action: This intent is used to broadcast the {@link UUID}
-     * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
-     * has been fetched. This intent is sent only when the UUIDs of the remote
-     * device are requested to be fetched using Service Discovery Protocol
-     * <p> Always contains the extra field {@link #EXTRA_DEVICE}
-     * <p> Always contains the extra field {@link #EXTRA_UUID}
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_UUID =
-            "android.bluetooth.device.action.UUID";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MAS_INSTANCE =
-            "android.bluetooth.device.action.MAS_INSTANCE";
-
-    /**
-     * Broadcast Action: Indicates a failure to retrieve the name of a remote
-     * device.
-     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
-     *
-     * @hide
-     */
-    //TODO: is this actually useful?
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NAME_FAILED =
-            "android.bluetooth.device.action.NAME_FAILED";
-
-    /**
-     * Broadcast Action: This intent is used to broadcast PAIRING REQUEST
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PAIRING_REQUEST =
-            "android.bluetooth.device.action.PAIRING_REQUEST";
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage
-    public static final String ACTION_PAIRING_CANCEL =
-            "android.bluetooth.device.action.PAIRING_CANCEL";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_ACCESS_REQUEST =
-            "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_ACCESS_REPLY =
-            "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_ACCESS_CANCEL =
-            "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
-
-    /**
-     * Intent to broadcast silence mode changed.
-     * Alway contains the extra field {@link #EXTRA_DEVICE}
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi
-    public static final String ACTION_SILENCE_MODE_CHANGED =
-            "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_ACCESS_REQUEST_TYPE =
-            "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE";
-
-    /** @hide */
-    public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1;
-
-    /** @hide */
-    public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2;
-
-    /** @hide */
-    public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3;
-
-    /** @hide */
-    public static final int REQUEST_TYPE_SIM_ACCESS = 4;
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
-     * Contains package name to return reply intent to.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
-     * Contains class name to return reply intent to.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME";
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_CONNECTION_ACCESS_RESULT =
-            "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT";
-
-    /** @hide */
-    public static final int CONNECTION_ACCESS_YES = 1;
-
-    /** @hide */
-    public static final int CONNECTION_ACCESS_NO = 2;
-
-    /**
-     * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents,
-     * Contains boolean to indicate if the allowed response is once-for-all so that
-     * next request will be granted without asking user again.
-     *
-     * @hide
-     */
-    public static final String EXTRA_ALWAYS_ALLOWED =
-            "android.bluetooth.device.extra.ALWAYS_ALLOWED";
-
-    /**
-     * A bond attempt succeeded
-     *
-     * @hide
-     */
-    public static final int BOND_SUCCESS = 0;
-
-    /**
-     * A bond attempt failed because pins did not match, or remote device did
-     * not respond to pin request in time
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_AUTH_FAILED = 1;
-
-    /**
-     * A bond attempt failed because the other side explicitly rejected
-     * bonding
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_AUTH_REJECTED = 2;
-
-    /**
-     * A bond attempt failed because we canceled the bonding process
-     *
-     * @hide
-     */
-    public static final int UNBOND_REASON_AUTH_CANCELED = 3;
-
-    /**
-     * A bond attempt failed because we could not contact the remote device
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
-
-    /**
-     * A bond attempt failed because a discovery is in progress
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
-
-    /**
-     * A bond attempt failed because of authentication timeout
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_AUTH_TIMEOUT = 6;
-
-    /**
-     * A bond attempt failed because of repeated attempts
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7;
-
-    /**
-     * A bond attempt failed because we received an Authentication Cancel
-     * by remote end
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
-
-    /**
-     * An existing bond was explicitly revoked
-     *
-     * @hide
-     */
-    public static final int UNBOND_REASON_REMOVED = 9;
-
-    /**
-     * The user will be prompted to enter a pin or
-     * an app will enter a pin for user.
-     */
-    public static final int PAIRING_VARIANT_PIN = 0;
-
-    /**
-     * The user will be prompted to enter a passkey
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_PASSKEY = 1;
-
-    /**
-     * The user will be prompted to confirm the passkey displayed on the screen or
-     * an app will confirm the passkey for the user.
-     */
-    public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2;
-
-    /**
-     * The user will be prompted to accept or deny the incoming pairing request
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_CONSENT = 3;
-
-    /**
-     * The user will be prompted to enter the passkey displayed on remote device
-     * This is used for Bluetooth 2.1 pairing.
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
-
-    /**
-     * The user will be prompted to enter the PIN displayed on remote device.
-     * This is used for Bluetooth 2.0 pairing.
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_DISPLAY_PIN = 5;
-
-    /**
-     * The user will be prompted to accept or deny the OOB pairing request
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_OOB_CONSENT = 6;
-
-    /**
-     * The user will be prompted to enter a 16 digit pin or
-     * an app will enter a 16 digit pin for user.
-     *
-     * @hide
-     */
-    public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7;
-
-    /**
-     * Used as an extra field in {@link #ACTION_UUID} intents,
-     * Contains the {@link android.os.ParcelUuid}s of the remote device which
-     * is a parcelable version of {@link UUID}.
-     */
-    public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
-
-    /** @hide */
-    public static final String EXTRA_SDP_RECORD =
-            "android.bluetooth.device.extra.SDP_RECORD";
-
-    /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final String EXTRA_SDP_SEARCH_STATUS =
-            "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
-
-    /** @hide */
-    @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN,
-            ACCESS_ALLOWED, ACCESS_REJECTED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AccessPermission{}
-
-    /**
-     * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
-     * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACCESS_UNKNOWN = 0;
-
-    /**
-     * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
-     * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACCESS_ALLOWED = 1;
-
-    /**
-     * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
-     * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ACCESS_REJECTED = 2;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "TRANSPORT_" },
-        value = {
-            /** Allow host to automatically select a transport (dual-mode only) */
-            TRANSPORT_AUTO,
-            /** Use Classic or BR/EDR transport.*/
-            TRANSPORT_BREDR,
-            /** Use Low Energy transport.*/
-            TRANSPORT_LE,
-        }
-    )
-    public @interface Transport {}
-
-    /**
-     * No preference of physical transport for GATT connections to remote dual-mode devices
-     */
-    public static final int TRANSPORT_AUTO = 0;
-
-    /**
-     * Prefer BR/EDR transport for GATT connections to remote dual-mode devices
-     */
-    public static final int TRANSPORT_BREDR = 1;
-
-    /**
-     * Prefer LE transport for GATT connections to remote dual-mode devices
-     */
-    public static final int TRANSPORT_LE = 2;
-
-    /**
-     * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or
-     * connection.
-     */
-    public static final int PHY_LE_1M = 1;
-
-    /**
-     * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or
-     * connection.
-     */
-    public static final int PHY_LE_2M = 2;
-
-    /**
-     * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning
-     * or connection.
-     */
-    public static final int PHY_LE_CODED = 3;
-
-    /**
-     * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available
-     * options in a bitmask.
-     */
-    public static final int PHY_LE_1M_MASK = 1;
-
-    /**
-     * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available
-     * options in a bitmask.
-     */
-    public static final int PHY_LE_2M_MASK = 2;
-
-    /**
-     * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many
-     * available options in a bitmask.
-     */
-    public static final int PHY_LE_CODED_MASK = 4;
-
-    /**
-     * No preferred coding when transmitting on the LE Coded PHY.
-     */
-    public static final int PHY_OPTION_NO_PREFERRED = 0;
-
-    /**
-     * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY.
-     */
-    public static final int PHY_OPTION_S2 = 1;
-
-    /**
-     * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY.
-     */
-    public static final int PHY_OPTION_S8 = 2;
-
-
-    /** @hide */
-    public static final String EXTRA_MAS_INSTANCE =
-            "android.bluetooth.device.extra.MAS_INSTANCE";
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "ADDRESS_TYPE_" },
-        value = {
-            /** Hardware MAC Address */
-            ADDRESS_TYPE_PUBLIC,
-            /** Address is either resolvable, non-resolvable or static.*/
-            ADDRESS_TYPE_RANDOM,
-        }
-    )
-    public @interface AddressType {}
-
-    /** Hardware MAC Address of the device */
-    public static final int ADDRESS_TYPE_PUBLIC = 0;
-    /** Address is either resolvable, non-resolvable or static. */
-    public static final int ADDRESS_TYPE_RANDOM = 1;
-
-    private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00";
-
-    /**
-     * Lazy initialization. Guaranteed final after first object constructed, or
-     * getService() called.
-     * TODO: Unify implementation of sService amongst BluetoothFoo API's
-     */
-    private static volatile IBluetooth sService;
-
-    private final String mAddress;
-    @AddressType private final int mAddressType;
-
-    private AttributionSource mAttributionSource;
-
-    /*package*/
-    @UnsupportedAppUsage
-    static IBluetooth getService() {
-        synchronized (BluetoothDevice.class) {
-            if (sService == null) {
-                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-                sService = adapter.getBluetoothService(sStateChangeCallback);
-            }
-        }
-        return sService;
-    }
-
-    static IBluetoothManagerCallback sStateChangeCallback = new IBluetoothManagerCallback.Stub() {
-
-        public void onBluetoothServiceUp(IBluetooth bluetoothService)
-                throws RemoteException {
-            synchronized (BluetoothDevice.class) {
-                if (sService == null) {
-                    sService = bluetoothService;
-                }
-            }
-        }
-
-        public void onBluetoothServiceDown()
-                throws RemoteException {
-            synchronized (BluetoothDevice.class) {
-                sService = null;
-            }
-        }
-
-        public void onBrEdrDown() {
-            if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
-        }
-
-        public void onOobData(@Transport int transport, OobData oobData) {
-            if (DBG) Log.d(TAG, "onOobData: got data");
-        }
-    };
-
-    /**
-     * Create a new BluetoothDevice
-     * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
-     * and is validated in this constructor.
-     *
-     * @param address valid Bluetooth MAC address
-     * @param attributionSource attribution for permission-protected calls
-     * @throws RuntimeException Bluetooth is not available on this platform
-     * @throws IllegalArgumentException address is invalid
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /*package*/ BluetoothDevice(String address) {
-        getService();  // ensures sService is initialized
-        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
-            throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
-        }
-
-        mAddress = address;
-        mAddressType = ADDRESS_TYPE_PUBLIC;
-        mAttributionSource = AttributionSource.myAttributionSource();
-    }
-
-    /** {@hide} */
-    public void setAttributionSource(@NonNull AttributionSource attributionSource) {
-        mAttributionSource = attributionSource;
-    }
-
-    /** {@hide} */
-    public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) {
-        setAttributionSource(attributionSource);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothDevice) {
-            return mAddress.equals(((BluetoothDevice) o).getAddress());
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mAddress.hashCode();
-    }
-
-    /**
-     * Returns a string representation of this BluetoothDevice.
-     * <p>Currently this is the Bluetooth hardware address, for example
-     * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
-     * if you explicitly require the Bluetooth hardware address in case the
-     * {@link #toString} representation changes in the future.
-     *
-     * @return string representation of this BluetoothDevice
-     */
-    @Override
-    public String toString() {
-        return mAddress;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothDevice> CREATOR =
-            new Parcelable.Creator<BluetoothDevice>() {
-                public BluetoothDevice createFromParcel(Parcel in) {
-                    return new BluetoothDevice(in.readString());
-                }
-
-                public BluetoothDevice[] newArray(int size) {
-                    return new BluetoothDevice[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mAddress);
-    }
-
-    /**
-     * Returns the hardware address of this BluetoothDevice.
-     * <p> For example, "00:11:22:AA:BB:CC".
-     *
-     * @return Bluetooth hardware address as string
-     */
-    public String getAddress() {
-        if (DBG) Log.d(TAG, "mAddress: " + mAddress);
-        return mAddress;
-    }
-
-    /**
-     * Returns the anonymized hardware address of this BluetoothDevice. The first three octets
-     * will be suppressed for anonymization.
-     * <p> For example, "XX:XX:XX:AA:BB:CC".
-     *
-     * @return Anonymized bluetooth hardware address as string
-     * @hide
-     */
-    public String getAnonymizedAddress() {
-        return "XX:XX:XX" + getAddress().substring(8);
-    }
-
-    /**
-     * Get the friendly Bluetooth name of the remote device.
-     *
-     * <p>The local adapter will automatically retrieve remote names when
-     * performing a device scan, and will cache them. This method just returns
-     * the name for this device from the cache.
-     *
-     * @return the Bluetooth name, or null if there was a problem.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getName() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Remote Device name");
-            return null;
-        }
-        try {
-            String name = service.getRemoteName(this, mAttributionSource);
-            if (name != null) {
-                // remove whitespace characters from the name
-                return name
-                        .replace('\t', ' ')
-                        .replace('\n', ' ')
-                        .replace('\r', ' ');
-            }
-            return null;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Get the Bluetooth device type of the remote device.
-     *
-     * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link
-     * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getType() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Remote Device type");
-            return DEVICE_TYPE_UNKNOWN;
-        }
-        try {
-            return service.getRemoteType(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return DEVICE_TYPE_UNKNOWN;
-    }
-
-    /**
-     * Get the locally modifiable name (alias) of the remote Bluetooth device.
-     *
-     * @return the Bluetooth alias, the friendly device name if no alias, or
-     * null if there was a problem
-     */
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getAlias() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias");
-            return null;
-        }
-        try {
-            String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource);
-            if (alias == null) {
-                return getName();
-            }
-            return alias
-                    .replace('\t', ' ')
-                    .replace('\n', ' ')
-                    .replace('\r', ' ');
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
-    })
-    public @interface SetAliasReturnValues{}
-
-    /**
-     * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method
-     * overwrites the previously stored alias. The new alias is saved in local
-     * storage so that the change is preserved over power cycles.
-     *
-     * <p>This method requires the calling app to be associated with Companion Device Manager (see
-     * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest,
-     * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the
-     * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the
-     * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can
-     * bypass the Companion Device Manager association requirement as well as other permission
-     * requirements.
-     *
-     * @param alias is the new locally modifiable name for the remote Bluetooth device which must
-     *              be the empty string. If null, we clear the alias.
-     * @return whether the alias was successfully changed
-     * @throws IllegalArgumentException if the alias is the empty string
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @SetAliasReturnValues int setAlias(@Nullable String alias) {
-        if (alias != null && alias.isEmpty()) {
-            throw new IllegalArgumentException("alias cannot be the empty string");
-        }
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
-            return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-        }
-        try {
-            return service.setRemoteAlias(this, alias, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the most recent identified battery level of this Bluetooth device
-     *
-     * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if
-     * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does
-     * not have any battery reporting service, or return value is invalid
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getBatteryLevel() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level");
-            return BATTERY_LEVEL_BLUETOOTH_OFF;
-        }
-        try {
-            return service.getBatteryLevel(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return BATTERY_LEVEL_UNKNOWN;
-    }
-
-    /**
-     * Start the bonding (pairing) process with the remote device.
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
-     * the bonding process completes, and its result.
-     * <p>Android system services will handle the necessary user interactions
-     * to confirm and complete the bonding process.
-     *
-     * @return false on immediate error, true if bonding will begin
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean createBond() {
-        return createBond(TRANSPORT_AUTO);
-    }
-
-    /**
-     * Start the bonding (pairing) process with the remote device using the
-     * specified transport.
-     *
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
-     * the bonding process completes, and its result.
-     * <p>Android system services will handle the necessary user interactions
-     * to confirm and complete the bonding process.
-     *
-     * @param transport The transport to use for the pairing procedure.
-     * @return false on immediate error, true if bonding will begin
-     * @throws IllegalArgumentException if an invalid transport was specified
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean createBond(int transport) {
-        return createBondInternal(transport, null, null);
-    }
-
-    /**
-     * Start the bonding (pairing) process with the remote device using the
-     * Out Of Band mechanism.
-     *
-     * <p>This is an asynchronous call, it will return immediately. Register
-     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
-     * the bonding process completes, and its result.
-     *
-     * <p>Android system services will handle the necessary user interactions
-     * to confirm and complete the bonding process.
-     *
-     * <p>There are two possible versions of OOB Data.  This data can come in as
-     * P192 or P256.  This is a reference to the cryptography used to generate the key.
-     * The caller may pass one or both.  If both types of data are passed, then the
-     * P256 data will be preferred, and thus used.
-     *
-     * @param transport - Transport to use
-     * @param remoteP192Data - Out Of Band data (P192) or null
-     * @param remoteP256Data - Out Of Band data (P256) or null
-     * @return false on immediate error, true if bonding will begin
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
-            @Nullable OobData remoteP256Data) {
-        if (remoteP192Data == null && remoteP256Data == null) {
-            throw new IllegalArgumentException(
-                "One or both arguments for the OOB data types are required to not be null."
-                + "  Please use createBond() instead if you do not have OOB data to pass.");
-        }
-        return createBondInternal(transport, remoteP192Data, remoteP256Data);
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
-            @Nullable OobData remoteP256Data) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
-            return false;
-        }
-        if (NULL_MAC_ADDRESS.equals(mAddress)) {
-            Log.e(TAG, "Unable to create bond, invalid address " + mAddress);
-            return false;
-        }
-        try {
-            return service.createBond(
-                    this, transport, remoteP192Data, remoteP256Data, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Gets whether bonding was initiated locally
-     *
-     * @return true if bonding is initiated locally, false otherwise
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isBondingInitiatedLocally() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed");
-            return false;
-        }
-        try {
-            return service.isBondingInitiatedLocally(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Cancel an in-progress bonding request started with {@link #createBond}.
-     *
-     * @return true on success, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean cancelBondProcess() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond");
-            return false;
-        }
-        try {
-            Log.i(TAG, "cancelBondProcess() for device " + getAddress()
-                    + " called by pid: " + Process.myPid()
-                    + " tid: " + Process.myTid());
-            return service.cancelBondProcess(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Remove bond (pairing) with the remote device.
-     * <p>Delete the link key associated with the remote device, and
-     * immediately terminate connections to that device that require
-     * authentication and encryption.
-     *
-     * @return true on success, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean removeBond() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond");
-            return false;
-        }
-        try {
-            Log.i(TAG, "removeBond() for device " + getAddress()
-                    + " called by pid: " + Process.myPid()
-                    + " tid: " + Process.myTid());
-            return service.removeBond(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
-            "cache_key.bluetooth.get_bond_state";
-    private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
-            new PropertyInvalidatedCache<BluetoothDevice, Integer>(
-                8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public Integer recompute(BluetoothDevice query) {
-                    try {
-                        return sService.getBondState(query, mAttributionSource);
-                    } catch (RemoteException e) {
-                        throw e.rethrowAsRuntimeException();
-                    }
-                }
-            };
-
-    /** @hide */
-    public void disableBluetoothGetBondStateCache() {
-        mBluetoothBondCache.disableLocal();
-    }
-
-    /** @hide */
-    public static void invalidateBluetoothGetBondStateCache() {
-        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
-    }
-
-    /**
-     * Get the bond state of the remote device.
-     * <p>Possible values for the bond state are:
-     * {@link #BOND_NONE},
-     * {@link #BOND_BONDING},
-     * {@link #BOND_BONDED}.
-     *
-     * @return the bond state
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public int getBondState() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get bond state");
-            return BOND_NONE;
-        }
-        try {
-            return mBluetoothBondCache.query(this);
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "", e);
-            } else {
-                throw e;
-            }
-        }
-        return BOND_NONE;
-    }
-
-    /**
-     * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip
-     * the bluetooth pairing dialog because it has been already consented by the CDM prompt.
-     *
-     * @return true if we can bond without the dialog, false otherwise
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean canBondWithoutDialog() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog");
-            return false;
-        }
-        try {
-            if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this);
-            return service.canBondWithoutDialog(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
-            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
-    })
-    public @interface ConnectionReturnValues{}
-
-    /**
-     * Connects all user enabled and supported bluetooth profiles between the local and remote
-     * device. If no profiles are user enabled (e.g. first connection), we connect all supported
-     * profiles. If the device is not already connected, this will page the device before initiating
-     * profile connections. Connection is asynchronous and you should listen to each profile's
-     * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful.
-     * For example, to verify a2dp is connected, you would listen for
-     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * @return whether the messages were successfully sent to try to connect all profiles
-     * @throws IllegalArgumentException if the device address is invalid
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public @ConnectionReturnValues int connect() {
-        if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
-            throw new IllegalArgumentException("device cannot have an invalid address");
-        }
-
-        try {
-            if (sService == null) {
-                Log.e(TAG, "BT not enabled. Cannot connect to remote device.");
-                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-            }
-            return sService.connectAllEnabledProfiles(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Disconnects all connected bluetooth profiles between the local and remote device.
-     * Disconnection is asynchronous and you should listen to each profile's broadcast intent
-     * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
-     * to verify a2dp is disconnected, you would listen for
-     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * @return whether the messages were successfully sent to try to disconnect all profiles
-     * @throws IllegalArgumentException if the device address is invalid
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionReturnValues int disconnect() {
-        if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
-            throw new IllegalArgumentException("device cannot have an invalid address");
-        }
-
-        try {
-            if (sService == null) {
-                Log.e(TAG, "BT not enabled. Cannot disconnect from remote device.");
-                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
-            }
-            return sService.disconnectAllEnabledProfiles(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Returns whether there is an open connection to this device.
-     *
-     * @return True if there is at least one open connection to this device.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            // BT is not enabled, we cannot be connected.
-            return false;
-        }
-        try {
-            return service.getConnectionStateWithAttribution(this, mAttributionSource)
-                    != CONNECTION_STATE_DISCONNECTED;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Returns whether there is an open connection to this device
-     * that has been encrypted.
-     *
-     * @return True if there is at least one encrypted connection to this device.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isEncrypted() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            // BT is not enabled, we cannot be connected.
-            return false;
-        }
-        try {
-            return service.getConnectionStateWithAttribution(this, mAttributionSource)
-                    > CONNECTION_STATE_CONNECTED;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get the Bluetooth class of the remote device.
-     *
-     * @return Bluetooth class object, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothClass getBluetoothClass() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class");
-            return null;
-        }
-        try {
-            int classInt = service.getRemoteClass(this, mAttributionSource);
-            if (classInt == BluetoothClass.ERROR) return null;
-            return new BluetoothClass(classInt);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Returns the supported features (UUIDs) of the remote device.
-     *
-     * <p>This method does not start a service discovery procedure to retrieve the UUIDs
-     * from the remote device. Instead, the local cached copy of the service
-     * UUIDs are returned.
-     * <p>Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired.
-     *
-     * @return the supported features (UUIDs) of the remote device, or null on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public ParcelUuid[] getUuids() {
-        final IBluetooth service = sService;
-        if (service == null || !isBluetoothEnabled()) {
-            Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
-            return null;
-        }
-        try {
-            return service.getRemoteUuids(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Perform a service discovery on the remote device to get the UUIDs supported.
-     *
-     * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
-     * with the UUIDs supported by the remote end. If there is an error
-     * in getting the SDP records or if the process takes a long time, or the device is bonding and
-     * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is
-     * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs
-     * if service discovery is not to be performed. If there is an ongoing bonding process,
-     * service discovery or device inquiry, the request will be queued.
-     *
-     * @return False if the check fails, True if the process of initiating an ACL connection
-     * to the remote device was started or cached UUIDs will be broadcast.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean fetchUuidsWithSdp() {
-        return fetchUuidsWithSdp(TRANSPORT_AUTO);
-    }
-
-    /**
-     * Perform a service discovery on the remote device to get the UUIDs supported with the
-     * specific transport.
-     *
-     * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
-     * with the UUIDs supported by the remote end. If there is an error
-     * in getting the SDP or GATT records or if the process takes a long time, or the device
-     * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the
-     * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids}
-     * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding
-     * process, service discovery or device inquiry, the request will be queued.
-     *
-     * @param transport - provide type of transport (e.g. LE or Classic).
-     * @return False if the check fails, True if the process of initiating an ACL connection
-     * to the remote device was started or cached UUIDs will be broadcast with the specific
-     * transport.
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean fetchUuidsWithSdp(@Transport int transport) {
-        final IBluetooth service = sService;
-        if (service == null || !isBluetoothEnabled()) {
-            Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
-            return false;
-        }
-        try {
-            return service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Perform a service discovery on the remote device to get the SDP records associated
-     * with the specified UUID.
-     *
-     * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent,
-     * with the SDP records found on the remote end. If there is an error
-     * in getting the SDP records or if the process takes a long time,
-     * {@link #ACTION_SDP_RECORD} intent is sent with an status value in
-     * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
-     * Detailed status error codes can be found by members of the Bluetooth package in
-     * the AbstractionLayer class.
-     * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
-     * The object type will match one of the SdpXxxRecord types, depending on the UUID searched
-     * for.
-     *
-     * @return False if the check fails, True if the process
-     *               of initiating an ACL connection to the remote device
-     *               was started.
-     */
-    /** @hide */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sdpSearch(ParcelUuid uuid) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
-            return false;
-        }
-        try {
-            return service.sdpSearch(this, uuid, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
-     *
-     * @return true pin has been set false for error
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPin(byte[] pin) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot set Remote Device pin");
-            return false;
-        }
-        try {
-            return service.setPin(this, true, pin.length, pin, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
-     *
-     * @return true pin has been set false for error
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPin(@NonNull String pin) {
-        byte[] pinBytes = convertPinToBytes(pin);
-        if (pinBytes == null) {
-            return false;
-        }
-        return setPin(pinBytes);
-    }
-
-    /**
-     * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing.
-     *
-     * @return true confirmation has been sent out false for error
-     */
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPairingConfirmation(boolean confirm) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot set pairing confirmation");
-            return false;
-        }
-        try {
-            return service.setPairingConfirmation(this, confirm, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Cancels pairing to this device
-     *
-     * @return true if pairing cancelled successfully, false otherwise
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean cancelPairing() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot cancel pairing");
-            return false;
-        }
-        try {
-            return service.cancelBondProcess(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    boolean isBluetoothEnabled() {
-        boolean ret = false;
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null && adapter.isEnabled()) {
-            ret = true;
-        }
-        return ret;
-    }
-
-    /**
-     * Gets whether the phonebook access is allowed for this bluetooth device
-     *
-     * @return Whether the phonebook access is allowed to this device. Can be {@link
-     * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @AccessPermission int getPhonebookAccessPermission() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return ACCESS_UNKNOWN;
-        }
-        try {
-            return service.getPhonebookAccessPermission(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return ACCESS_UNKNOWN;
-    }
-
-    /**
-     * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not
-     * be routed to the {@link BluetoothDevice} if set to {@code true}.
-     *
-     * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice}
-     * is an active device (for A2DP or HFP), the active device for that profile
-     * will be set to null.
-     * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP
-     * active device is null, the {@link BluetoothDevice} will be set as the
-     * active device for that profile.
-     * If the {@link BluetoothDevice} is disconnected, it exits silence mode.
-     * If the {@link BluetoothDevice} is set as the active device for A2DP or
-     * HFP, while silence mode is enabled, then the device will exit silence mode.
-     * If the {@link BluetoothDevice} is in silence mode, AVRCP position change
-     * event and HFP AG indicators will be disabled.
-     * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
-     * enter silence mode.
-     *
-     * @param silence true to enter silence mode, false to exit
-     * @return true on success, false on error.
-     * @throws IllegalStateException if Bluetooth is not turned ON.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setSilenceMode(boolean silence) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            throw new IllegalStateException("Bluetooth is not turned ON");
-        }
-        try {
-            return service.setSilenceMode(this, silence, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "setSilenceMode fail", e);
-            return false;
-        }
-    }
-
-    /**
-     * Check whether the {@link BluetoothDevice} is in silence mode
-     *
-     * @return true on device in silence mode, otherwise false.
-     * @throws IllegalStateException if Bluetooth is not turned ON.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean isInSilenceMode() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            throw new IllegalStateException("Bluetooth is not turned ON");
-        }
-        try {
-            return service.getSilenceMode(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "isInSilenceMode fail", e);
-            return false;
-        }
-    }
-
-    /**
-     * Sets whether the phonebook access is allowed to this device.
-     *
-     * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
-     * #ACCESS_REJECTED}.
-     * @return Whether the value has been successfully set.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPhonebookAccessPermission(@AccessPermission int value) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.setPhonebookAccessPermission(this, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Gets whether message access is allowed to this bluetooth device
-     *
-     * @return Whether the message access is allowed to this device.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @AccessPermission int getMessageAccessPermission() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return ACCESS_UNKNOWN;
-        }
-        try {
-            return service.getMessageAccessPermission(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return ACCESS_UNKNOWN;
-    }
-
-    /**
-     * Sets whether the message access is allowed to this device.
-     *
-     * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded,
-     * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if
-     * the permission is not being granted.
-     * @return Whether the value has been successfully set.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setMessageAccessPermission(@AccessPermission int value) {
-        // Validates param value is one of the accepted constants
-        if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
-            throw new IllegalArgumentException(value + "is not a valid AccessPermission value");
-        }
-        final IBluetooth service = sService;
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.setMessageAccessPermission(this, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Gets whether sim access is allowed for this bluetooth device
-     *
-     * @return Whether the Sim access is allowed to this device.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @AccessPermission int getSimAccessPermission() {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return ACCESS_UNKNOWN;
-        }
-        try {
-            return service.getSimAccessPermission(this, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return ACCESS_UNKNOWN;
-    }
-
-    /**
-     * Sets whether the Sim access is allowed to this device.
-     *
-     * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded,
-     * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if
-     * the permission is not being granted.
-     * @return Whether the value has been successfully set.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setSimAccessPermission(int value) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            return false;
-        }
-        try {
-            return service.setSimAccessPermission(this, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
-     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
-     * outgoing connection to this remote device on given channel.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p> Use this socket only if an authenticated socket link is possible.
-     * Authentication refers to the authentication of the link key to
-     * prevent person-in-the-middle type of attacks.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not
-     * have an input and output capability or just has the ability to
-     * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {@link createInsecureRfcommSocket}.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection.
-     * <p>Valid RFCOMM channels are in range 1 to 30.
-     *
-     * @param channel RFCOMM channel to connect to
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createRfcommSocket(int channel) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
-                null);
-    }
-
-    /**
-     * Create an L2cap {@link BluetoothSocket} ready to start a secure
-     * outgoing connection to this remote device on given channel.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p> Use this socket only if an authenticated socket link is possible.
-     * Authentication refers to the authentication of the link key to
-     * prevent person-in-the-middle type of attacks.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not
-     * have an input and output capability or just has the ability to
-     * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {@link createInsecureRfcommSocket}.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection.
-     * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
-     *
-     * @param channel L2cap PSM/channel to connect to
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createL2capSocket(int channel) throws IOException {
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
-                null);
-    }
-
-    /**
-     * Create an L2cap {@link BluetoothSocket} ready to start an insecure
-     * outgoing connection to this remote device on given channel.
-     * <p>The remote device will be not authenticated and communication on this
-     * socket will not be encrypted.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection.
-     * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
-     *
-     * @param channel L2cap PSM/channel to connect to
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException {
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel,
-                null);
-    }
-
-    /**
-     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
-     * outgoing connection to this remote device using SDP lookup of uuid.
-     * <p>This is designed to be used with {@link
-     * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
-     * Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection. This will also perform an SDP lookup of the given uuid to
-     * determine which channel to connect to.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p> Use this socket only if an authenticated socket link is possible.
-     * Authentication refers to the authentication of the link key to
-     * prevent person-in-the-middle type of attacks.
-     * For example, for Bluetooth 2.1 devices, if any of the devices does not
-     * have an input and output capability or just has the ability to
-     * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}.
-     * For more details, refer to the Security Model section 5.2 (vol 3) of
-     * Bluetooth Core Specification version 2.1 + EDR.
-     * <p>Hint: If you are connecting to a Bluetooth serial board then try
-     * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
-     * However if you are connecting to an Android peer then please generate
-     * your own unique UUID.
-     *
-     * @param uuid service record uuid to lookup RFCOMM channel
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
-                new ParcelUuid(uuid));
-    }
-
-    /**
-     * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure
-     * outgoing connection to this remote device using SDP lookup of uuid.
-     * <p> The communication channel will not have an authenticated link key
-     * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1
-     * devices, the link key will be encrypted, as encryption is mandatory.
-     * For legacy devices (pre Bluetooth 2.1 devices) the link key will
-     * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an
-     * encrypted and authenticated communication channel is desired.
-     * <p>This is designed to be used with {@link
-     * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer
-     * Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
-     * connection. This will also perform an SDP lookup of the given uuid to
-     * determine which channel to connect to.
-     * <p>The remote device will be authenticated and communication on this
-     * socket will be encrypted.
-     * <p>Hint: If you are connecting to a Bluetooth serial board then try
-     * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
-     * However if you are connecting to an Android peer then please generate
-     * your own unique UUID.
-     *
-     * @param uuid service record uuid to lookup RFCOMM channel
-     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
-                new ParcelUuid(uuid));
-    }
-
-    /**
-     * Construct an insecure RFCOMM socket ready to start an outgoing
-     * connection.
-     * Call #connect on the returned #BluetoothSocket to begin the connection.
-     * The remote device will not be authenticated and communication on this
-     * socket will not be encrypted.
-     *
-     * @param port remote port
-     * @return An RFCOMM BluetoothSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @UnsupportedAppUsage(publicAlternatives = "Use "
-            + "{@link #createInsecureRfcommSocketToServiceRecord} instead.")
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
-                null);
-    }
-
-    /**
-     * Construct a SCO socket ready to start an outgoing connection.
-     * Call #connect on the returned #BluetoothSocket to begin the connection.
-     *
-     * @return a SCO BluetoothSocket
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public BluetoothSocket createScoSocket() throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "Bluetooth is not enabled");
-            throw new IOException();
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
-    }
-
-    /**
-     * Check that a pin is valid and convert to byte array.
-     *
-     * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters.
-     *
-     * @param pin pin as java String
-     * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static byte[] convertPinToBytes(String pin) {
-        if (pin == null) {
-            return null;
-        }
-        byte[] pinBytes;
-        try {
-            pinBytes = pin.getBytes("UTF-8");
-        } catch (UnsupportedEncodingException uee) {
-            Log.e(TAG, "UTF-8 not supported?!?");  // this should not happen
-            return null;
-        }
-        if (pinBytes.length <= 0 || pinBytes.length > 16) {
-            return null;
-        }
-        return pinBytes;
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @throws IllegalArgumentException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback) {
-        return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO));
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @throws IllegalArgumentException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport) {
-        return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK));
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
-     * is set to true.
-     * @throws NullPointerException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport, int phy) {
-        return connectGatt(context, autoConnect, callback, transport, phy, null);
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
-     * is set to true.
-     * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
-     * an un-specified background thread.
-     * @throws NullPointerException if callback is null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport, int phy,
-            Handler handler) {
-        return connectGatt(context, autoConnect, callback, transport, false, phy, handler);
-    }
-
-    /**
-     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as any further GATT client operations.
-     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
-     * GATT client operations.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client
-     * does not hold a GATT connection. It automatically disconnects when no other GATT connections
-     * are active for the remote device.
-     * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
-     * is set to true.
-     * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
-     * an un-specified background thread.
-     * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client
-     * operations.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
-            BluetoothGattCallback callback, int transport,
-            boolean opportunistic, int phy, Handler handler) {
-        if (callback == null) {
-            throw new NullPointerException("callback is null");
-        }
-
-        // TODO(Bluetooth) check whether platform support BLE
-        //     Do the check here or in GattServer?
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        IBluetoothManager managerService = adapter.getBluetoothManager();
-        try {
-            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported
-                return null;
-            }
-            BluetoothGatt gatt = new BluetoothGatt(
-                    iGatt, this, transport, opportunistic, phy, mAttributionSource);
-            gatt.connect(autoConnect, callback, handler);
-            return gatt;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return null;
-    }
-
-    /**
-     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
-     * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
-     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
-     * peer-peer Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
-     * <p>Application using this API is responsible for obtaining PSM value from remote device.
-     * <p>The remote device will be authenticated and communication on this socket will be
-     * encrypted.
-     * <p> Use this socket if an authenticated socket link is possible. Authentication refers
-     * to the authentication of the link key to prevent person-in-the-middle type of attacks.
-     *
-     * @param psm dynamic PSM value from remote device
-     * @return a CoC #BluetoothSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
-            throw new IOException();
-        }
-        if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
-                null);
-    }
-
-    /**
-     * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
-     * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
-     * <p>This is designed to be used with {@link
-     * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
-     * <p>Application using this API is responsible for obtaining PSM value from remote device.
-     * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
-     * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
-     * authenticated communication channel is possible.
-     *
-     * @param psm dynamic PSM value from remote device
-     * @return a CoC #BluetoothSocket ready for an outgoing connection
-     * @throws IOException on error, for example Bluetooth not available, or insufficient
-     * permissions
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
-            throw new IOException();
-        }
-        if (DBG) {
-            Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
-                null);
-    }
-
-    /**
-     * Set a keyed metadata of this {@link BluetoothDevice} to a
-     * {@link String} value.
-     * Only bonded devices's metadata will be persisted across Bluetooth
-     * restart.
-     * Metadata will be removed when the device's bond state is moved to
-     * {@link #BOND_NONE}.
-     *
-     * @param key must be within the list of BluetoothDevice.METADATA_*
-     * @param value a byte array data to set for key. Must be less than
-     * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
-     * @return true on success, false on error
-     * @hide
-    */
-    @SystemApi
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
-            return false;
-        }
-        if (value.length > METADATA_MAX_LENGTH) {
-            throw new IllegalArgumentException("value length is " + value.length
-                    + ", should not over " + METADATA_MAX_LENGTH);
-        }
-        try {
-            return service.setMetadata(this, key, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "setMetadata fail", e);
-            return false;
-        }
-    }
-
-    /**
-     * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
-     *
-     * @param key must be within the list of BluetoothDevice.METADATA_*
-     * @return Metadata of the key as byte array, null on error or not found
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public byte[] getMetadata(@MetadataKey int key) {
-        final IBluetooth service = sService;
-        if (service == null) {
-            Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
-            return null;
-        }
-        try {
-            return service.getMetadata(this, key, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "getMetadata fail", e);
-            return null;
-        }
-    }
-
-    /**
-     * Get the maxinum metadata key ID.
-     *
-     * @return the last supported metadata key
-     * @hide
-     */
-    public static @MetadataKey int getMaxMetadataKey() {
-        return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
deleted file mode 100644
index 26e4657..0000000
--- a/core/java/android/bluetooth/BluetoothDevicePicker.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-
-/**
- * A helper to show a system "Device Picker" activity to the user.
- *
- * @hide
- */
-public interface BluetoothDevicePicker {
-    public static final String EXTRA_NEED_AUTH =
-            "android.bluetooth.devicepicker.extra.NEED_AUTH";
-    public static final String EXTRA_FILTER_TYPE =
-            "android.bluetooth.devicepicker.extra.FILTER_TYPE";
-    public static final String EXTRA_LAUNCH_PACKAGE =
-            "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE";
-    public static final String EXTRA_LAUNCH_CLASS =
-            "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS";
-
-    /**
-     * Broadcast when one BT device is selected from BT device picker screen.
-     * Selected {@link BluetoothDevice} is returned in extra data named
-     * {@link BluetoothDevice#EXTRA_DEVICE}.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DEVICE_SELECTED =
-            "android.bluetooth.devicepicker.action.DEVICE_SELECTED";
-
-    /**
-     * Broadcast when someone want to select one BT device from devices list.
-     * This intent contains below extra data:
-     * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication
-     * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be
-     * listed
-     * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this
-     * intent come from
-     * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent
-     * come from
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LAUNCH =
-            "android.bluetooth.devicepicker.action.LAUNCH";
-
-    /** Ask device picker to show all kinds of BT devices */
-    public static final int FILTER_TYPE_ALL = 0;
-    /** Ask device picker to show BT devices that support AUDIO profiles */
-    public static final int FILTER_TYPE_AUDIO = 1;
-    /** Ask device picker to show BT devices that support Object Transfer */
-    public static final int FILTER_TYPE_TRANSFER = 2;
-    /**
-     * Ask device picker to show BT devices that support
-     * Personal Area Networking User (PANU) profile
-     */
-    public static final int FILTER_TYPE_PANU = 3;
-    /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */
-    public static final int FILTER_TYPE_NAP = 4;
-}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
deleted file mode 100644
index b531829..0000000
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ /dev/null
@@ -1,1848 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.os.Build;
-import android.os.Handler;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Public API for the Bluetooth GATT Profile.
- *
- * <p>This class provides Bluetooth GATT functionality to enable communication
- * with Bluetooth Smart or Smart Ready devices.
- *
- * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
- * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
- * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
- * scan process.
- */
-public final class BluetoothGatt implements BluetoothProfile {
-    private static final String TAG = "BluetoothGatt";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    @UnsupportedAppUsage
-    private IBluetoothGatt mService;
-    @UnsupportedAppUsage
-    private volatile BluetoothGattCallback mCallback;
-    private Handler mHandler;
-    @UnsupportedAppUsage
-    private int mClientIf;
-    private BluetoothDevice mDevice;
-    @UnsupportedAppUsage
-    private boolean mAutoConnect;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private int mAuthRetryState;
-    private int mConnState;
-    private final Object mStateLock = new Object();
-    private final Object mDeviceBusyLock = new Object();
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private Boolean mDeviceBusy = false;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private int mTransport;
-    private int mPhy;
-    private boolean mOpportunistic;
-    private final AttributionSource mAttributionSource;
-
-    private static final int AUTH_RETRY_STATE_IDLE = 0;
-    private static final int AUTH_RETRY_STATE_NO_MITM = 1;
-    private static final int AUTH_RETRY_STATE_MITM = 2;
-
-    private static final int CONN_STATE_IDLE = 0;
-    private static final int CONN_STATE_CONNECTING = 1;
-    private static final int CONN_STATE_CONNECTED = 2;
-    private static final int CONN_STATE_DISCONNECTING = 3;
-    private static final int CONN_STATE_CLOSED = 4;
-
-    private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
-    private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds
-
-    private List<BluetoothGattService> mServices;
-
-    /** A GATT operation completed successfully */
-    public static final int GATT_SUCCESS = 0;
-
-    /** GATT read operation is not permitted */
-    public static final int GATT_READ_NOT_PERMITTED = 0x2;
-
-    /** GATT write operation is not permitted */
-    public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
-
-    /** Insufficient authentication for a given operation */
-    public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
-
-    /** The given request is not supported */
-    public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
-
-    /** Insufficient encryption for a given operation */
-    public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
-
-    /** A read or write operation was requested with an invalid offset */
-    public static final int GATT_INVALID_OFFSET = 0x7;
-
-    /** Insufficient authorization for a given operation */
-    public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8;
-
-    /** A write operation exceeds the maximum length of the attribute */
-    public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
-
-    /** A remote device connection is congested. */
-    public static final int GATT_CONNECTION_CONGESTED = 0x8f;
-
-    /** A GATT operation failed, errors other than the above */
-    public static final int GATT_FAILURE = 0x101;
-
-    /**
-     * Connection parameter update - Use the connection parameters recommended by the
-     * Bluetooth SIG. This is the default value if no connection parameter update
-     * is requested.
-     */
-    public static final int CONNECTION_PRIORITY_BALANCED = 0;
-
-    /**
-     * Connection parameter update - Request a high priority, low latency connection.
-     * An application should only request high priority connection parameters to transfer large
-     * amounts of data over LE quickly. Once the transfer is complete, the application should
-     * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce
-     * energy use.
-     */
-    public static final int CONNECTION_PRIORITY_HIGH = 1;
-
-    /** Connection parameter update - Request low power, reduced data rate connection parameters. */
-    public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
-
-    /**
-     * No authentication required.
-     *
-     * @hide
-     */
-    /*package*/ static final int AUTHENTICATION_NONE = 0;
-
-    /**
-     * Authentication requested; no person-in-the-middle protection required.
-     *
-     * @hide
-     */
-    /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
-
-    /**
-     * Authentication with person-in-the-middle protection requested.
-     *
-     * @hide
-     */
-    /*package*/ static final int AUTHENTICATION_MITM = 2;
-
-    /**
-     * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
-     */
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothGattCallback mBluetoothGattCallback =
-            new IBluetoothGattCallback.Stub() {
-                /**
-                 * Application interface registered - app is ready to go
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onClientRegistered(int status, int clientIf) {
-                    if (DBG) {
-                        Log.d(TAG, "onClientRegistered() - status=" + status
-                                + " clientIf=" + clientIf);
-                    }
-                    if (VDBG) {
-                        synchronized (mStateLock) {
-                            if (mConnState != CONN_STATE_CONNECTING) {
-                                Log.e(TAG, "Bad connection state: " + mConnState);
-                            }
-                        }
-                    }
-                    mClientIf = clientIf;
-                    if (status != GATT_SUCCESS) {
-                        runOrQueueCallback(new Runnable() {
-                            @Override
-                            public void run() {
-                                final BluetoothGattCallback callback = mCallback;
-                                if (callback != null) {
-                                    callback.onConnectionStateChange(BluetoothGatt.this,
-                                            GATT_FAILURE,
-                                            BluetoothProfile.STATE_DISCONNECTED);
-                                }
-                            }
-                        });
-
-                        synchronized (mStateLock) {
-                            mConnState = CONN_STATE_IDLE;
-                        }
-                        return;
-                    }
-                    try {
-                        mService.clientConnect(mClientIf, mDevice.getAddress(),
-                                !mAutoConnect, mTransport, mOpportunistic,
-                                mPhy, mAttributionSource); // autoConnect is inverse of "isDirect"
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "", e);
-                    }
-                }
-
-                /**
-                 * Phy update callback
-                 * @hide
-                 */
-                @Override
-                public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onPhyUpdate() - status=" + status
-                                + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Phy read callback
-                 * @hide
-                 */
-                @Override
-                public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onPhyRead() - status=" + status
-                                + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Client connection state changed
-                 * @hide
-                 */
-                @Override
-                public void onClientConnectionState(int status, int clientIf,
-                        boolean connected, String address) {
-                    if (DBG) {
-                        Log.d(TAG, "onClientConnectionState() - status=" + status
-                                + " clientIf=" + clientIf + " device=" + address);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-                    int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
-                            BluetoothProfile.STATE_DISCONNECTED;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onConnectionStateChange(BluetoothGatt.this, status,
-                                        profileState);
-                            }
-                        }
-                    });
-
-                    synchronized (mStateLock) {
-                        if (connected) {
-                            mConnState = CONN_STATE_CONNECTED;
-                        } else {
-                            mConnState = CONN_STATE_IDLE;
-                        }
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-                }
-
-                /**
-                 * Remote search has been completed.
-                 * The internal object structure should now reflect the state
-                 * of the remote device database. Let the application know that
-                 * we are done at this point.
-                 * @hide
-                 */
-                @Override
-                public void onSearchComplete(String address, List<BluetoothGattService> services,
-                        int status) {
-                    if (DBG) {
-                        Log.d(TAG,
-                                "onSearchComplete() = Device=" + address + " Status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    for (BluetoothGattService s : services) {
-                        //services we receive don't have device set properly.
-                        s.setDevice(mDevice);
-                    }
-
-                    mServices.addAll(services);
-
-                    // Fix references to included services, as they doesn't point to right objects.
-                    for (BluetoothGattService fixedService : mServices) {
-                        ArrayList<BluetoothGattService> includedServices =
-                                new ArrayList(fixedService.getIncludedServices());
-                        fixedService.getIncludedServices().clear();
-
-                        for (BluetoothGattService brokenRef : includedServices) {
-                            BluetoothGattService includedService = getService(mDevice,
-                                    brokenRef.getUuid(), brokenRef.getInstanceId());
-                            if (includedService != null) {
-                                fixedService.addIncludedService(includedService);
-                            } else {
-                                Log.e(TAG, "Broken GATT database: can't find included service.");
-                            }
-                        }
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onServicesDiscovered(BluetoothGatt.this, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Remote characteristic has been read.
-                 * Updates the internal value.
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onCharacteristicRead(String address, int status, int handle,
-                        byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG, "onCharacteristicRead() - Device=" + address
-                                + " handle=" + handle + " Status=" + status);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.readCharacteristic(
-                                    mClientIf, address, handle, authReq, mAttributionSource);
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
-                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
-                            handle);
-                    if (characteristic == null) {
-                        Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                if (status == 0) characteristic.setValue(value);
-                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
-                                        value, status);
-                                // Keep calling deprecated callback to maintain app compatibility
-                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
-                                        status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Characteristic has been written to the remote device.
-                 * Let the app know how we did...
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onCharacteristicWrite(String address, int status, int handle,
-                        byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG, "onCharacteristicWrite() - Device=" + address
-                                + " handle=" + handle + " Status=" + status);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
-                            handle);
-                    if (characteristic == null) return;
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
-                            for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
-                                requestStatus =  mService.writeCharacteristic(mClientIf, address,
-                                                  handle, characteristic.getWriteType(), authReq,
-                                                  value, mAttributionSource);
-                                if (requestStatus
-                                        != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
-                                    break;
-                                }
-                                try {
-                                    Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
-                                } catch (InterruptedException e) {
-                                }
-                            }
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
-                                        status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Remote characteristic has been updated.
-                 * Updates the internal value.
-                 * @hide
-                 */
-                @Override
-                public void onNotify(String address, int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
-                            handle);
-                    if (characteristic == null) return;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                characteristic.setValue(value);
-                                callback.onCharacteristicChanged(BluetoothGatt.this,
-                                        characteristic, value);
-                                // Keep calling deprecated callback to maintain app compatibility
-                                callback.onCharacteristicChanged(BluetoothGatt.this,
-                                        characteristic);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Descriptor has been read.
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onDescriptorRead(String address, int status, int handle, byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG,
-                                "onDescriptorRead() - Device=" + address + " handle=" + handle);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
-                    if (descriptor == null) return;
-
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.readDescriptor(
-                                    mClientIf, address, handle, authReq, mAttributionSource);
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                if (status == 0) descriptor.setValue(value);
-                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status,
-                                        value);
-                                // Keep calling deprecated callback to maintain app compatibility
-                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Descriptor write operation complete.
-                 * @hide
-                 */
-                @Override
-                @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onDescriptorWrite(String address, int status, int handle,
-                        byte[] value) {
-                    if (VDBG) {
-                        Log.d(TAG,
-                                "onDescriptorWrite() - Device=" + address + " handle=" + handle);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
-                    if (descriptor == null) return;
-
-                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
-                            || status == GATT_INSUFFICIENT_ENCRYPTION)
-                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
-                        try {
-                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
-                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            mService.writeDescriptor(mClientIf, address, handle,
-                                    authReq, value, mAttributionSource);
-                            mAuthRetryState++;
-                            return;
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "", e);
-                        }
-                    }
-
-                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Prepared write transaction completed (or aborted)
-                 * @hide
-                 */
-                @Override
-                public void onExecuteWrite(String address, int status) {
-                    if (VDBG) {
-                        Log.d(TAG, "onExecuteWrite() - Device=" + address
-                                + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    synchronized (mDeviceBusyLock) {
-                        mDeviceBusy = false;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onReliableWriteCompleted(BluetoothGatt.this, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Remote device RSSI has been read
-                 * @hide
-                 */
-                @Override
-                public void onReadRemoteRssi(String address, int rssi, int status) {
-                    if (VDBG) {
-                        Log.d(TAG, "onReadRemoteRssi() - Device=" + address
-                                + " rssi=" + rssi + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Callback invoked when the MTU for a given connection changes
-                 * @hide
-                 */
-                @Override
-                public void onConfigureMTU(String address, int mtu, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onConfigureMTU() - Device=" + address
-                                + " mtu=" + mtu + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onMtuChanged(BluetoothGatt.this, mtu, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Callback invoked when the given connection is updated
-                 * @hide
-                 */
-                @Override
-                public void onConnectionUpdated(String address, int interval, int latency,
-                        int timeout, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onConnectionUpdated() - Device=" + address
-                                + " interval=" + interval + " latency=" + latency
-                                + " timeout=" + timeout + " status=" + status);
-                    }
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
-                                        timeout, status);
-                            }
-                        }
-                    });
-                }
-
-                /**
-                 * Callback invoked when service changed event is received
-                 * @hide
-                 */
-                @Override
-                public void onServiceChanged(String address) {
-                    if (DBG) {
-                        Log.d(TAG, "onServiceChanged() - Device=" + address);
-                    }
-
-                    if (!address.equals(mDevice.getAddress())) {
-                        return;
-                    }
-
-                    runOrQueueCallback(new Runnable() {
-                        @Override
-                        public void run() {
-                            final BluetoothGattCallback callback = mCallback;
-                            if (callback != null) {
-                                callback.onServiceChanged(BluetoothGatt.this);
-                            }
-                        }
-                    });
-                }
-            };
-
-    /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport,
-            boolean opportunistic, int phy, AttributionSource attributionSource) {
-        mService = iGatt;
-        mDevice = device;
-        mTransport = transport;
-        mPhy = phy;
-        mOpportunistic = opportunistic;
-        mAttributionSource = attributionSource;
-        mServices = new ArrayList<BluetoothGattService>();
-
-        mConnState = CONN_STATE_IDLE;
-        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-    }
-
-    /**
-     * Close this Bluetooth GATT client.
-     *
-     * Application should call this method as early as possible after it is done with
-     * this GATT client.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void close() {
-        if (DBG) Log.d(TAG, "close()");
-
-        unregisterApp();
-        mConnState = CONN_STATE_CLOSED;
-        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-    }
-
-    /**
-     * Returns a service by UUID, instance and type.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
-            int instanceId) {
-        for (BluetoothGattService svc : mServices) {
-            if (svc.getDevice().equals(device)
-                    && svc.getInstanceId() == instanceId
-                    && svc.getUuid().equals(uuid)) {
-                return svc;
-            }
-        }
-        return null;
-    }
-
-
-    /**
-     * Returns a characteristic with id equal to instanceId.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
-            int instanceId) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                if (charac.getInstanceId() == instanceId) {
-                    return charac;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns a descriptor with id equal to instanceId.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
-                    if (desc.getInstanceId() == instanceId) {
-                        return desc;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
-     * immediately if no Handler was provided.
-     */
-    private void runOrQueueCallback(final Runnable cb) {
-        if (mHandler == null) {
-            try {
-                cb.run();
-            } catch (Exception ex) {
-                Log.w(TAG, "Unhandled exception in callback", ex);
-            }
-        } else {
-            mHandler.post(cb);
-        }
-    }
-
-    /**
-     * Register an application callback to start using GATT.
-     *
-     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
-     * is used to notify success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @return If true, the callback will be called to notify success or failure, false on immediate
-     * error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
-        return registerApp(callback, handler, false);
-    }
-
-    /**
-     * Register an application callback to start using GATT.
-     *
-     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
-     * is used to notify success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param eatt_support indicate to allow for eatt support
-     * @return If true, the callback will be called to notify success or failure, false on immediate
-     * error
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean registerApp(BluetoothGattCallback callback, Handler handler,
-                                boolean eatt_support) {
-        if (DBG) Log.d(TAG, "registerApp()");
-        if (mService == null) return false;
-
-        mCallback = callback;
-        mHandler = handler;
-        UUID uuid = UUID.randomUUID();
-        if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
-
-        try {
-            mService.registerClient(
-                    new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Unregister the current application and callbacks.
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void unregisterApp() {
-        if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mCallback = null;
-            mService.unregisterClient(mClientIf, mAttributionSource);
-            mClientIf = 0;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Initiate a connection to a Bluetooth GATT capable device.
-     *
-     * <p>The connection may not be established right away, but will be
-     * completed when the remote device is available. A
-     * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
-     * invoked when the connection state changes as a result of this function.
-     *
-     * <p>The autoConnect parameter determines whether to actively connect to
-     * the remote device, or rather passively scan and finalize the connection
-     * when the remote device is in range/available. Generally, the first ever
-     * connection to a device should be direct (autoConnect set to false) and
-     * subsequent connections to known devices should be invoked with the
-     * autoConnect parameter set to true.
-     *
-     * @param device Remote device to connect to
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @return true, if the connection attempt was initiated successfully
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
-            Handler handler) {
-        if (DBG) {
-            Log.d(TAG,
-                    "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
-        }
-        synchronized (mStateLock) {
-            if (mConnState != CONN_STATE_IDLE) {
-                throw new IllegalStateException("Not idle");
-            }
-            mConnState = CONN_STATE_CONNECTING;
-        }
-
-        mAutoConnect = autoConnect;
-
-        if (!registerApp(callback, handler)) {
-            synchronized (mStateLock) {
-                mConnState = CONN_STATE_IDLE;
-            }
-            Log.e(TAG, "Failed to register callback");
-            return false;
-        }
-
-        // The connection will continue in the onClientRegistered callback
-        return true;
-    }
-
-    /**
-     * Disconnects an established connection, or cancels a connection attempt
-     * currently in progress.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void disconnect() {
-        if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Connect back to remote device.
-     *
-     * <p>This method is used to re-connect to a remote device after the
-     * connection has been dropped. If the device is not in range, the
-     * re-connection will be triggered once the device is back in range.
-     *
-     * @return true, if the connection attempt was initiated successfully
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect() {
-        try {
-            // autoConnect is inverse of "isDirect"
-            mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
-                    mOpportunistic, mPhy, mAttributionSource);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Set the preferred connection PHY for this app. Please note that this is just a
-     * recommendation, whether the PHY change will happen depends on other applications preferences,
-     * local and remote controller capabilities. Controller can override these settings.
-     * <p>
-     * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
-     * if no PHY change happens. It is also triggered when remote device updates the PHY.
-     *
-     * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
-     * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
-     * {@link BluetoothDevice#PHY_OPTION_S8}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
-        try {
-            mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
-                    phyOptions, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
-     * in {@link BluetoothGattCallback#onPhyRead}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void readPhy() {
-        try {
-            mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Return the remote bluetooth device this GATT client targets to
-     *
-     * @return remote bluetooth device
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Discovers services offered by a remote device as well as their
-     * characteristics and descriptors.
-     *
-     * <p>This is an asynchronous operation. Once service discovery is completed,
-     * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
-     * triggered. If the discovery was successful, the remote services can be
-     * retrieved using the {@link #getServices} function.
-     *
-     * @return true, if the remote service discovery has been started
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean discoverServices() {
-        if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        mServices.clear();
-
-        try {
-            mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Discovers a service by UUID. This is exposed only for passing PTS tests.
-     * It should never be used by real applications. The service is not searched
-     * for characteristics and descriptors, or returned in any callback.
-     *
-     * @return true, if the remote service discovery has been started
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean discoverServiceByUuid(UUID uuid) {
-        if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        mServices.clear();
-
-        try {
-            mService.discoverServiceByUuid(
-                    mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns a list of GATT services offered by the remote device.
-     *
-     * <p>This function requires that service discovery has been completed
-     * for the given device.
-     *
-     * @return List of services on the remote device. Returns an empty list if service discovery has
-     * not yet been performed.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public List<BluetoothGattService> getServices() {
-        List<BluetoothGattService> result =
-                new ArrayList<BluetoothGattService>();
-
-        for (BluetoothGattService service : mServices) {
-            if (service.getDevice().equals(mDevice)) {
-                result.add(service);
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Returns a {@link BluetoothGattService}, if the requested UUID is
-     * supported by the remote device.
-     *
-     * <p>This function requires that service discovery has been completed
-     * for the given device.
-     *
-     * <p>If multiple instances of the same service (as identified by UUID)
-     * exist, the first instance of the service is returned.
-     *
-     * @param uuid UUID of the requested service
-     * @return BluetoothGattService if supported, or null if the requested service is not offered by
-     * the remote device.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public BluetoothGattService getService(UUID uuid) {
-        for (BluetoothGattService service : mServices) {
-            if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
-                return service;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Reads the requested characteristic from the associated remote device.
-     *
-     * <p>This is an asynchronous operation. The result of the read operation
-     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[], int)} callback.
-     *
-     * @param characteristic Characteristic to read from the remote device
-     * @return true, if the read operation was initiated successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
-        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
-            return false;
-        }
-
-        if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
-        if (mService == null || mClientIf == 0) return false;
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.readCharacteristic(mClientIf, device.getAddress(),
-                    characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Reads the characteristic using its UUID from the associated remote device.
-     *
-     * <p>This is an asynchronous operation. The result of the read operation
-     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[], int)} callback.
-     *
-     * @param uuid UUID of characteristic to read from the remote device
-     * @return true, if the read operation was initiated successfully
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
-        if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
-        if (mService == null || mClientIf == 0) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
-                    new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE,
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-
-    /**
-     * Writes a given characteristic and its values to the associated remote device.
-     *
-     * <p>Once the write operation has been completed, the
-     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
-     * reporting the result of the operation.
-     *
-     * @param characteristic Characteristic to write on the remote device
-     * @return true, if the write operation was initiated successfully
-     * @throws IllegalArgumentException if characteristic or its value are null
-     *
-     * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
-     * int)} as this is not memory safe.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
-        try {
-            return writeCharacteristic(characteristic, characteristic.getValue(),
-                    characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
-            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
-            BluetoothStatusCodes.ERROR_UNKNOWN
-    })
-    public @interface WriteOperationReturnValues{}
-
-    /**
-     * Writes a given characteristic and its values to the associated remote device.
-     *
-     * <p>Once the write operation has been completed, the
-     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
-     * reporting the result of the operation.
-     *
-     * @param characteristic Characteristic to write on the remote device
-     * @return whether the characteristic was successfully written to
-     * @throws IllegalArgumentException if characteristic or value are null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @WriteOperationReturnValues
-    public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic,
-            @NonNull byte[] value, int writeType) {
-        if (characteristic == null) {
-            throw new IllegalArgumentException("characteristic must not be null");
-        }
-        if (value == null) {
-            throw new IllegalArgumentException("value must not be null");
-        }
-        if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
-        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
-                && (characteristic.getProperties()
-                & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
-            return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
-        }
-        if (mService == null || mClientIf == 0) {
-            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
-        }
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) {
-            throw new IllegalArgumentException("Characteristic must have a non-null service");
-        }
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) {
-            throw new IllegalArgumentException("Service must have a non-null device");
-        }
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) {
-                return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
-            }
-            mDeviceBusy = true;
-        }
-
-        int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
-        try {
-            for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
-                requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
-                        characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value,
-                        mAttributionSource);
-                if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
-                    break;
-                }
-                try {
-                    Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
-                } catch (InterruptedException e) {
-                }
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            throw e.rethrowFromSystemServer();
-        }
-
-        return requestStatus;
-    }
-
-    /**
-     * Reads the value for a given descriptor from the associated remote device.
-     *
-     * <p>Once the read operation has been completed, the
-     * {@link BluetoothGattCallback#onDescriptorRead} callback is
-     * triggered, signaling the result of the operation.
-     *
-     * @param descriptor Descriptor value to read from the remote device
-     * @return true, if the read operation was initiated successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
-        if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
-        if (mService == null || mClientIf == 0) return false;
-
-        BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
-        if (characteristic == null) return false;
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.readDescriptor(mClientIf, device.getAddress(),
-                    descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Write the value of a given descriptor to the associated remote device.
-     *
-     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
-     * result of the write operation.
-     *
-     * @param descriptor Descriptor to write to the associated remote device
-     * @return true, if the write operation was initiated successfully
-     * @throws IllegalArgumentException if descriptor or its value are null
-     *
-     * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
-     * this is not memory safe.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
-        try {
-            return writeDescriptor(descriptor, descriptor.getValue())
-                    == BluetoothStatusCodes.SUCCESS;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /**
-     * Write the value of a given descriptor to the associated remote device.
-     *
-     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
-     * result of the write operation.
-     *
-     * @param descriptor Descriptor to write to the associated remote device
-     * @return true, if the write operation was initiated successfully
-     * @throws IllegalArgumentException if descriptor or value are null
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @WriteOperationReturnValues
-    public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor,
-            @NonNull byte[] value) {
-        if (descriptor == null) {
-            throw new IllegalArgumentException("descriptor must not be null");
-        }
-        if (value == null) {
-            throw new IllegalArgumentException("value must not be null");
-        }
-        if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
-        if (mService == null || mClientIf == 0) {
-            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
-        }
-
-        BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
-        if (characteristic == null) {
-            throw new IllegalArgumentException("Descriptor must have a non-null characteristic");
-        }
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) {
-            throw new IllegalArgumentException("Characteristic must have a non-null service");
-        }
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) {
-            throw new IllegalArgumentException("Service must have a non-null device");
-        }
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
-            mDeviceBusy = true;
-        }
-
-        try {
-            return mService.writeDescriptor(mClientIf, device.getAddress(),
-                    descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            e.rethrowFromSystemServer();
-        }
-        return BluetoothStatusCodes.ERROR_UNKNOWN;
-    }
-
-    /**
-     * Initiates a reliable write transaction for a given remote device.
-     *
-     * <p>Once a reliable write transaction has been initiated, all calls
-     * to {@link #writeCharacteristic} are sent to the remote device for
-     * verification and queued up for atomic execution. The application will
-     * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every
-     * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is
-     * responsible for verifying if the value has been transmitted accurately.
-     *
-     * <p>After all characteristics have been queued up and verified,
-     * {@link #executeReliableWrite} will execute all writes. If a characteristic
-     * was not written correctly, calling {@link #abortReliableWrite} will
-     * cancel the current transaction without committing any values on the
-     * remote device.
-     *
-     * @return true, if the reliable write transaction has been initiated
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean beginReliableWrite() {
-        if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Executes a reliable write transaction for a given remote device.
-     *
-     * <p>This function will commit all queued up characteristic write
-     * operations for a given remote device.
-     *
-     * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
-     * invoked to indicate whether the transaction has been executed correctly.
-     *
-     * @return true, if the request to execute the transaction has been sent
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean executeReliableWrite() {
-        if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
-            mDeviceBusy = true;
-        }
-
-        try {
-            mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            synchronized (mDeviceBusyLock) {
-                mDeviceBusy = false;
-            }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Cancels a reliable write transaction for a given device.
-     *
-     * <p>Calling this function will discard all queued characteristic write
-     * operations for a given remote device.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void abortReliableWrite() {
-        if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * @deprecated Use {@link #abortReliableWrite()}
-     */
-    @Deprecated
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void abortReliableWrite(BluetoothDevice mDevice) {
-        abortReliableWrite();
-    }
-
-    /**
-     * Enable or disable notifications/indications for a given characteristic.
-     *
-     * <p>Once notifications are enabled for a characteristic, a
-     * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device
-     * indicates that the given characteristic has changed.
-     *
-     * @param characteristic The characteristic for which to enable notifications
-     * @param enable Set to true to enable notifications/indications
-     * @return true, if the requested notification status was set successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
-            boolean enable) {
-        if (DBG) {
-            Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
-                    + " enable: " + enable);
-        }
-        if (mService == null || mClientIf == 0) return false;
-
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
-
-        BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
-
-        try {
-            mService.registerForNotification(mClientIf, device.getAddress(),
-                    characteristic.getInstanceId(), enable, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Clears the internal cache and forces a refresh of the services from the
-     * remote device.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean refresh() {
-        if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Read the RSSI for a connected remote device.
-     *
-     * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
-     * invoked when the RSSI value has been read.
-     *
-     * @return true, if the RSSI value has been requested successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean readRemoteRssi() {
-        if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Request an MTU size used for a given connection.
-     *
-     * <p>When performing a write request operation (write without response),
-     * the data sent is truncated to the MTU size. This function may be used
-     * to request a larger MTU size to be able to send more data at once.
-     *
-     * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
-     * whether this operation was successful.
-     *
-     * @return true, if the new MTU value has been requested successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean requestMtu(int mtu) {
-        if (DBG) {
-            Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
-                    + " mtu: " + mtu);
-        }
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Request a connection parameter update.
-     *
-     * <p>This function will send a connection parameter update request to the
-     * remote device.
-     *
-     * @param connectionPriority Request a specific connection priority. Must be one of {@link
-     * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
-     * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
-     * @throws IllegalArgumentException If the parameters are outside of their specified range.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean requestConnectionPriority(int connectionPriority) {
-        if (connectionPriority < CONNECTION_PRIORITY_BALANCED
-                || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
-            throw new IllegalArgumentException("connectionPriority not within valid range");
-        }
-
-        if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.connectionParameterUpdate(
-                    mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Request an LE connection parameter update.
-     *
-     * <p>This function will send an LE connection parameters update request to the remote device.
-     *
-     * @return true, if the request is send to the Bluetooth stack.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
-                                             int slaveLatency, int supervisionTimeout,
-                                             int minConnectionEventLen, int maxConnectionEventLen) {
-        if (DBG) {
-            Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
-                        + ")" + (1.25 * minConnectionInterval)
-                        + "msec, max=(" + maxConnectionInterval + ")"
-                        + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
-                        + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
-                        + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
-        }
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
-                    minConnectionInterval, maxConnectionInterval,
-                    slaveLatency, supervisionTimeout,
-                    minConnectionEventLen, maxConnectionEventLen,
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    @Deprecated
-    public int getConnectionState(BluetoothDevice device) {
-        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
-    }
-
-    /**
-     * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    @Deprecated
-    public List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getConnectedDevices instead.");
-    }
-
-    /**
-     * @deprecated Not supported - please use
-     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
-     * with {@link BluetoothProfile#GATT} as first argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    @Deprecated
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
deleted file mode 100644
index d0a5a1e..0000000
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.NonNull;
-
-/**
- * This abstract class is used to implement {@link BluetoothGatt} callbacks.
- */
-public abstract class BluetoothGattCallback {
-
-    /**
-     * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of
-     * remote device changing the PHY.
-     *
-     * @param gatt GATT client
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback triggered as result of {@link BluetoothGatt#readPhy}
-     *
-     * @param gatt GATT client
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
-     * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback indicating when GATT client has connected/disconnected to/from a remote
-     * GATT server.
-     *
-     * @param gatt GATT client
-     * @param status Status of the connect or disconnect operation. {@link
-     * BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
-     * @param newState Returns the new connection state. Can be one of {@link
-     * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED}
-     */
-    public void onConnectionStateChange(BluetoothGatt gatt, int status,
-            int newState) {
-    }
-
-    /**
-     * Callback invoked when the list of remote services, characteristics and descriptors
-     * for the remote device have been updated, ie new services have been discovered.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device has been explored
-     * successfully.
-     */
-    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
-    }
-
-    /**
-     * Callback reporting the result of a characteristic read operation.
-     *
-     * @param gatt           GATT client invoked
-     *                       {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
-     * @param characteristic Characteristic that was read from the associated remote device.
-     * @param status         {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                       successfully.
-     * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[], int)} as it is memory safe
-     */
-    @Deprecated
-    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
-            int status) {
-    }
-
-    /**
-     * Callback reporting the result of a characteristic read operation.
-     *
-     * @param gatt           GATT client invoked
-     *                       {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
-     * @param characteristic Characteristic that was read from the associated remote device.
-     * @param value          the value of the characteristic
-     * @param status         {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                       successfully.
-     */
-    public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull
-            BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
-    }
-
-    /**
-     * Callback indicating the result of a characteristic write operation.
-     *
-     * <p>If this callback is invoked while a reliable write transaction is
-     * in progress, the value of the characteristic represents the value
-     * reported by the remote device. An application should compare this
-     * value to the desired value to be written. If the values don't match,
-     * the application must abort the reliable write transaction.
-     *
-     * @param gatt           GATT client that invoked
-     *                       {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic,
-     *                       byte[], int)}
-     * @param characteristic Characteristic that was written to the associated remote device.
-     * @param status         The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if
-     *                       the
-     *                       operation succeeds.
-     */
-    public void onCharacteristicWrite(BluetoothGatt gatt,
-            BluetoothGattCharacteristic characteristic, int status) {
-    }
-
-    /**
-     * Callback triggered as a result of a remote characteristic notification.
-     *
-     * @param gatt           GATT client the characteristic is associated with
-     * @param characteristic Characteristic that has been updated as a result of a remote
-     *                       notification event.
-     * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
-     * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic
-     * value at the time of notification.
-     */
-    @Deprecated
-    public void onCharacteristicChanged(BluetoothGatt gatt,
-            BluetoothGattCharacteristic characteristic) {
-    }
-
-    /**
-     * Callback triggered as a result of a remote characteristic notification. Note that the value
-     * within the characteristic object may have changed since receiving the remote characteristic
-     * notification, so check the parameter value for the value at the time of notification.
-     *
-     * @param gatt           GATT client the characteristic is associated with
-     * @param characteristic Characteristic that has been updated as a result of a remote
-     *                       notification event.
-     * @param value          notified characteristic value
-     */
-    public void onCharacteristicChanged(@NonNull BluetoothGatt gatt,
-            @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
-    }
-
-    /**
-     * Callback reporting the result of a descriptor read operation.
-     *
-     * @param gatt       GATT client invoked {@link BluetoothGatt#readDescriptor}
-     * @param descriptor Descriptor that was read from the associated remote device.
-     * @param status     {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                   successfully
-     * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt,
-     * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor
-     * value at the time it was read.
-     */
-    @Deprecated
-    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
-            int status) {
-    }
-
-    /**
-     * Callback reporting the result of a descriptor read operation.
-     *
-     * @param gatt       GATT client invoked {@link BluetoothGatt#readDescriptor}
-     * @param descriptor Descriptor that was read from the associated remote device.
-     * @param status     {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     *                   successfully
-     * @param value      the descriptor value at the time of the read operation
-     */
-    public void onDescriptorRead(@NonNull BluetoothGatt gatt,
-            @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
-    }
-
-    /**
-     * Callback indicating the result of a descriptor write operation.
-     *
-     * @param gatt       GATT client invoked {@link BluetoothGatt#writeDescriptor}
-     * @param descriptor Descriptor that was writte to the associated remote device.
-     * @param status     The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
-     *                   operation succeeds.
-     */
-    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
-            int status) {
-    }
-
-    /**
-     * Callback invoked when a reliable write transaction has been completed.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write transaction was
-     * executed successfully
-     */
-    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
-    }
-
-    /**
-     * Callback reporting the RSSI for a remote device connection.
-     *
-     * This callback is triggered in response to the
-     * {@link BluetoothGatt#readRemoteRssi} function.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
-     * @param rssi The RSSI value for the remote device
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
-     */
-    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
-    }
-
-    /**
-     * Callback indicating the MTU for a given device connection has changed.
-     *
-     * This callback is triggered in response to the
-     * {@link BluetoothGatt#requestMtu} function, or in response to a connection
-     * event.
-     *
-     * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu}
-     * @param mtu The new MTU size
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully
-     */
-    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
-    }
-
-    /**
-     * Callback indicating the connection parameters were updated.
-     *
-     * @param gatt GATT client involved
-     * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
-     * 6 (7.5ms) to 3200 (4000ms).
-     * @param latency Worker latency for the connection in number of connection events. Valid range
-     * is from 0 to 499
-     * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
-     * (0.1s) to 3200 (32s)
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated
-     * successfully
-     * @hide
-     */
-    public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout,
-            int status) {
-    }
-
-    /**
-     * Callback indicating service changed event is received
-     *
-     * <p>Receiving this event means that the GATT database is out of sync with
-     * the remote device. {@link BluetoothGatt#discoverServices} should be
-     * called to re-discover the services.
-     *
-     * @param gatt GATT client involved
-     */
-    public void onServiceChanged(@NonNull BluetoothGatt gatt) {
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
deleted file mode 100644
index c5e986e..0000000
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Characteristic
- *
- * <p>A GATT characteristic is a basic data element used to construct a GATT service,
- * {@link BluetoothGattService}. The characteristic contains a value as well as
- * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}.
- */
-public class BluetoothGattCharacteristic implements Parcelable {
-
-    /**
-     * Characteristic proprty: Characteristic is broadcastable.
-     */
-    public static final int PROPERTY_BROADCAST = 0x01;
-
-    /**
-     * Characteristic property: Characteristic is readable.
-     */
-    public static final int PROPERTY_READ = 0x02;
-
-    /**
-     * Characteristic property: Characteristic can be written without response.
-     */
-    public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04;
-
-    /**
-     * Characteristic property: Characteristic can be written.
-     */
-    public static final int PROPERTY_WRITE = 0x08;
-
-    /**
-     * Characteristic property: Characteristic supports notification
-     */
-    public static final int PROPERTY_NOTIFY = 0x10;
-
-    /**
-     * Characteristic property: Characteristic supports indication
-     */
-    public static final int PROPERTY_INDICATE = 0x20;
-
-    /**
-     * Characteristic property: Characteristic supports write with signature
-     */
-    public static final int PROPERTY_SIGNED_WRITE = 0x40;
-
-    /**
-     * Characteristic property: Characteristic has extended properties
-     */
-    public static final int PROPERTY_EXTENDED_PROPS = 0x80;
-
-    /**
-     * Characteristic read permission
-     */
-    public static final int PERMISSION_READ = 0x01;
-
-    /**
-     * Characteristic permission: Allow encrypted read operations
-     */
-    public static final int PERMISSION_READ_ENCRYPTED = 0x02;
-
-    /**
-     * Characteristic permission: Allow reading with person-in-the-middle protection
-     */
-    public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
-
-    /**
-     * Characteristic write permission
-     */
-    public static final int PERMISSION_WRITE = 0x10;
-
-    /**
-     * Characteristic permission: Allow encrypted writes
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
-
-    /**
-     * Characteristic permission: Allow encrypted writes with person-in-the-middle
-     * protection
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
-
-    /**
-     * Characteristic permission: Allow signed write operations
-     */
-    public static final int PERMISSION_WRITE_SIGNED = 0x80;
-
-    /**
-     * Characteristic permission: Allow signed write operations with
-     * person-in-the-middle protection
-     */
-    public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
-
-    /**
-     * Write characteristic, requesting acknoledgement by the remote device
-     */
-    public static final int WRITE_TYPE_DEFAULT = 0x02;
-
-    /**
-     * Write characteristic without requiring a response by the remote device
-     */
-    public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
-
-    /**
-     * Write characteristic including authentication signature
-     */
-    public static final int WRITE_TYPE_SIGNED = 0x04;
-
-    /**
-     * Characteristic value format type uint8
-     */
-    public static final int FORMAT_UINT8 = 0x11;
-
-    /**
-     * Characteristic value format type uint16
-     */
-    public static final int FORMAT_UINT16 = 0x12;
-
-    /**
-     * Characteristic value format type uint32
-     */
-    public static final int FORMAT_UINT32 = 0x14;
-
-    /**
-     * Characteristic value format type sint8
-     */
-    public static final int FORMAT_SINT8 = 0x21;
-
-    /**
-     * Characteristic value format type sint16
-     */
-    public static final int FORMAT_SINT16 = 0x22;
-
-    /**
-     * Characteristic value format type sint32
-     */
-    public static final int FORMAT_SINT32 = 0x24;
-
-    /**
-     * Characteristic value format type sfloat (16-bit float)
-     */
-    public static final int FORMAT_SFLOAT = 0x32;
-
-    /**
-     * Characteristic value format type float (32-bit float)
-     */
-    public static final int FORMAT_FLOAT = 0x34;
-
-
-    /**
-     * The UUID of this characteristic.
-     *
-     * @hide
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this characteristic.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected int mInstance;
-
-    /**
-     * Characteristic properties.
-     *
-     * @hide
-     */
-    protected int mProperties;
-
-    /**
-     * Characteristic permissions.
-     *
-     * @hide
-     */
-    protected int mPermissions;
-
-    /**
-     * Key size (default = 16).
-     *
-     * @hide
-     */
-    protected int mKeySize = 16;
-
-    /**
-     * Write type for this characteristic.
-     * See WRITE_TYPE_* constants.
-     *
-     * @hide
-     */
-    protected int mWriteType;
-
-    /**
-     * Back-reference to the service this characteristic belongs to.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected BluetoothGattService mService;
-
-    /**
-     * The cached value of this characteristic.
-     *
-     * @hide
-     */
-    protected byte[] mValue;
-
-    /**
-     * List of descriptors included in this characteristic.
-     */
-    protected List<BluetoothGattDescriptor> mDescriptors;
-
-    /**
-     * Create a new BluetoothGattCharacteristic.
-     *
-     * @param uuid The UUID for this characteristic
-     * @param properties Properties of this characteristic
-     * @param permissions Permissions for this characteristic
-     */
-    public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
-        initCharacteristic(null, uuid, 0, properties, permissions);
-    }
-
-    /**
-     * Create a new BluetoothGattCharacteristic
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic(BluetoothGattService service,
-            UUID uuid, int instanceId,
-            int properties, int permissions) {
-        initCharacteristic(service, uuid, instanceId, properties, permissions);
-    }
-
-    /**
-     * Create a new BluetoothGattCharacteristic
-     *
-     * @hide
-     */
-    public BluetoothGattCharacteristic(UUID uuid, int instanceId,
-            int properties, int permissions) {
-        initCharacteristic(null, uuid, instanceId, properties, permissions);
-    }
-
-    private void initCharacteristic(BluetoothGattService service,
-            UUID uuid, int instanceId,
-            int properties, int permissions) {
-        mUuid = uuid;
-        mInstance = instanceId;
-        mProperties = properties;
-        mPermissions = permissions;
-        mService = service;
-        mValue = null;
-        mDescriptors = new ArrayList<BluetoothGattDescriptor>();
-
-        if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) {
-            mWriteType = WRITE_TYPE_NO_RESPONSE;
-        } else {
-            mWriteType = WRITE_TYPE_DEFAULT;
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstance);
-        out.writeInt(mProperties);
-        out.writeInt(mPermissions);
-        out.writeInt(mKeySize);
-        out.writeInt(mWriteType);
-        out.writeTypedList(mDescriptors);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattCharacteristic> CREATOR =
-            new Parcelable.Creator<BluetoothGattCharacteristic>() {
-        public BluetoothGattCharacteristic createFromParcel(Parcel in) {
-            return new BluetoothGattCharacteristic(in);
-        }
-
-        public BluetoothGattCharacteristic[] newArray(int size) {
-            return new BluetoothGattCharacteristic[size];
-        }
-    };
-
-    private BluetoothGattCharacteristic(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
-        mInstance = in.readInt();
-        mProperties = in.readInt();
-        mPermissions = in.readInt();
-        mKeySize = in.readInt();
-        mWriteType = in.readInt();
-
-        mDescriptors = new ArrayList<BluetoothGattDescriptor>();
-
-        ArrayList<BluetoothGattDescriptor> descs =
-                in.createTypedArrayList(BluetoothGattDescriptor.CREATOR);
-        if (descs != null) {
-            for (BluetoothGattDescriptor desc : descs) {
-                desc.setCharacteristic(this);
-                mDescriptors.add(desc);
-            }
-        }
-    }
-
-    /**
-     * Returns the desired key size.
-     *
-     * @hide
-     */
-    public int getKeySize() {
-        return mKeySize;
-    }
-
-    /**
-     * Adds a descriptor to this characteristic.
-     *
-     * @param descriptor Descriptor to be added to this characteristic.
-     * @return true, if the descriptor was added to the characteristic
-     */
-    public boolean addDescriptor(BluetoothGattDescriptor descriptor) {
-        mDescriptors.add(descriptor);
-        descriptor.setCharacteristic(this);
-        return true;
-    }
-
-    /**
-     * Get a descriptor by UUID and isntance id.
-     *
-     * @hide
-     */
-    /*package*/  BluetoothGattDescriptor getDescriptor(UUID uuid, int instanceId) {
-        for (BluetoothGattDescriptor descriptor : mDescriptors) {
-            if (descriptor.getUuid().equals(uuid)
-                    && descriptor.getInstanceId() == instanceId) {
-                return descriptor;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the service this characteristic belongs to.
-     *
-     * @return The asscociated service
-     */
-    public BluetoothGattService getService() {
-        return mService;
-    }
-
-    /**
-     * Sets the service associated with this device.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /*package*/ void setService(BluetoothGattService service) {
-        mService = service;
-    }
-
-    /**
-     * Returns the UUID of this characteristic
-     *
-     * @return UUID of this characteristic
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this characteristic.
-     *
-     * <p>If a remote device offers multiple characteristics with the same UUID,
-     * the instance ID is used to distuinguish between characteristics.
-     *
-     * @return Instance ID of this characteristic
-     */
-    public int getInstanceId() {
-        return mInstance;
-    }
-
-    /**
-     * Force the instance ID.
-     *
-     * @hide
-     */
-    public void setInstanceId(int instanceId) {
-        mInstance = instanceId;
-    }
-
-    /**
-     * Returns the properties of this characteristic.
-     *
-     * <p>The properties contain a bit mask of property flags indicating
-     * the features of this characteristic.
-     *
-     * @return Properties of this characteristic
-     */
-    public int getProperties() {
-        return mProperties;
-    }
-
-    /**
-     * Returns the permissions for this characteristic.
-     *
-     * @return Permissions of this characteristic
-     */
-    public int getPermissions() {
-        return mPermissions;
-    }
-
-    /**
-     * Gets the write type for this characteristic.
-     *
-     * @return Write type for this characteristic
-     */
-    public int getWriteType() {
-        return mWriteType;
-    }
-
-    /**
-     * Set the write type for this characteristic
-     *
-     * <p>Setting the write type of a characteristic determines how the
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function
-     * write this characteristic.
-     *
-     * @param writeType The write type to for this characteristic. Can be one of: {@link
-     * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}.
-     */
-    public void setWriteType(int writeType) {
-        mWriteType = writeType;
-    }
-
-    /**
-     * Set the desired key size.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void setKeySize(int keySize) {
-        mKeySize = keySize;
-    }
-
-    /**
-     * Returns a list of descriptors for this characteristic.
-     *
-     * @return Descriptors for this characteristic
-     */
-    public List<BluetoothGattDescriptor> getDescriptors() {
-        return mDescriptors;
-    }
-
-    /**
-     * Returns a descriptor with a given UUID out of the list of
-     * descriptors for this characteristic.
-     *
-     * @return GATT descriptor object or null if no descriptor with the given UUID was found.
-     */
-    public BluetoothGattDescriptor getDescriptor(UUID uuid) {
-        for (BluetoothGattDescriptor descriptor : mDescriptors) {
-            if (descriptor.getUuid().equals(uuid)) {
-                return descriptor;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Get the stored value for this characteristic.
-     *
-     * <p>This function returns the stored value for this characteristic as
-     * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached
-     * value of the characteristic is updated as a result of a read characteristic
-     * operation or if a characteristic update notification has been received.
-     *
-     * @return Cached value of the characteristic
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead
-     */
-    @Deprecated
-    public byte[] getValue() {
-        return mValue;
-    }
-
-    /**
-     * Return the stored value of this characteristic.
-     *
-     * <p>The formatType parameter determines how the characteristic value
-     * is to be interpreted. For example, settting formatType to
-     * {@link #FORMAT_UINT16} specifies that the first two bytes of the
-     * characteristic value at the given offset are interpreted to generate the
-     * return value.
-     *
-     * @param formatType The format type used to interpret the characteristic value.
-     * @param offset Offset at which the integer value can be found.
-     * @return Cached value of the characteristic or null of offset exceeds value size.
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
-     * the characteristic value
-     */
-    @Deprecated
-    public Integer getIntValue(int formatType, int offset) {
-        if ((offset + getTypeLen(formatType)) > mValue.length) return null;
-
-        switch (formatType) {
-            case FORMAT_UINT8:
-                return unsignedByteToInt(mValue[offset]);
-
-            case FORMAT_UINT16:
-                return unsignedBytesToInt(mValue[offset], mValue[offset + 1]);
-
-            case FORMAT_UINT32:
-                return unsignedBytesToInt(mValue[offset], mValue[offset + 1],
-                        mValue[offset + 2], mValue[offset + 3]);
-            case FORMAT_SINT8:
-                return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8);
-
-            case FORMAT_SINT16:
-                return unsignedToSigned(unsignedBytesToInt(mValue[offset],
-                        mValue[offset + 1]), 16);
-
-            case FORMAT_SINT32:
-                return unsignedToSigned(unsignedBytesToInt(mValue[offset],
-                        mValue[offset + 1], mValue[offset + 2], mValue[offset + 3]), 32);
-        }
-
-        return null;
-    }
-
-    /**
-     * Return the stored value of this characteristic.
-     * <p>See {@link #getValue} for details.
-     *
-     * @param formatType The format type used to interpret the characteristic value.
-     * @param offset Offset at which the float value can be found.
-     * @return Cached value of the characteristic at a given offset or null if the requested offset
-     * exceeds the value size.
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
-     * the characteristic value
-     */
-    @Deprecated
-    public Float getFloatValue(int formatType, int offset) {
-        if ((offset + getTypeLen(formatType)) > mValue.length) return null;
-
-        switch (formatType) {
-            case FORMAT_SFLOAT:
-                return bytesToFloat(mValue[offset], mValue[offset + 1]);
-
-            case FORMAT_FLOAT:
-                return bytesToFloat(mValue[offset], mValue[offset + 1],
-                        mValue[offset + 2], mValue[offset + 3]);
-        }
-
-        return null;
-    }
-
-    /**
-     * Return the stored value of this characteristic.
-     * <p>See {@link #getValue} for details.
-     *
-     * @param offset Offset at which the string value can be found.
-     * @return Cached value of the characteristic
-     *
-     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
-     * the characteristic value
-     */
-    @Deprecated
-    public String getStringValue(int offset) {
-        if (mValue == null || offset > mValue.length) return null;
-        byte[] strBytes = new byte[mValue.length - offset];
-        for (int i = 0; i != (mValue.length - offset); ++i) strBytes[i] = mValue[offset + i];
-        return new String(strBytes);
-    }
-
-    /**
-     * Updates the locally stored value of this characteristic.
-     *
-     * <p>This function modifies the locally stored cached value of this
-     * characteristic. To send the value to the remote device, call
-     * {@link BluetoothGatt#writeCharacteristic} to send the value to the
-     * remote device.
-     *
-     * @param value New value for this characteristic
-     * @return true if the locally stored value has been set, false if the requested value could not
-     * be stored locally.
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(byte[] value) {
-        mValue = value;
-        return true;
-    }
-
-    /**
-     * Set the locally stored value of this characteristic.
-     * <p>See {@link #setValue(byte[])} for details.
-     *
-     * @param value New value for this characteristic
-     * @param formatType Integer format type used to transform the value parameter
-     * @param offset Offset at which the value should be placed
-     * @return true if the locally stored value has been set
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(int value, int formatType, int offset) {
-        int len = offset + getTypeLen(formatType);
-        if (mValue == null) mValue = new byte[len];
-        if (len > mValue.length) return false;
-
-        switch (formatType) {
-            case FORMAT_SINT8:
-                value = intToSignedBits(value, 8);
-                // Fall-through intended
-            case FORMAT_UINT8:
-                mValue[offset] = (byte) (value & 0xFF);
-                break;
-
-            case FORMAT_SINT16:
-                value = intToSignedBits(value, 16);
-                // Fall-through intended
-            case FORMAT_UINT16:
-                mValue[offset++] = (byte) (value & 0xFF);
-                mValue[offset] = (byte) ((value >> 8) & 0xFF);
-                break;
-
-            case FORMAT_SINT32:
-                value = intToSignedBits(value, 32);
-                // Fall-through intended
-            case FORMAT_UINT32:
-                mValue[offset++] = (byte) (value & 0xFF);
-                mValue[offset++] = (byte) ((value >> 8) & 0xFF);
-                mValue[offset++] = (byte) ((value >> 16) & 0xFF);
-                mValue[offset] = (byte) ((value >> 24) & 0xFF);
-                break;
-
-            default:
-                return false;
-        }
-        return true;
-    }
-
-    /**
-     * Set the locally stored value of this characteristic.
-     * <p>See {@link #setValue(byte[])} for details.
-     *
-     * @param mantissa Mantissa for this characteristic
-     * @param exponent exponent value for this characteristic
-     * @param formatType Float format type used to transform the value parameter
-     * @param offset Offset at which the value should be placed
-     * @return true if the locally stored value has been set
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(int mantissa, int exponent, int formatType, int offset) {
-        int len = offset + getTypeLen(formatType);
-        if (mValue == null) mValue = new byte[len];
-        if (len > mValue.length) return false;
-
-        switch (formatType) {
-            case FORMAT_SFLOAT:
-                mantissa = intToSignedBits(mantissa, 12);
-                exponent = intToSignedBits(exponent, 4);
-                mValue[offset++] = (byte) (mantissa & 0xFF);
-                mValue[offset] = (byte) ((mantissa >> 8) & 0x0F);
-                mValue[offset] += (byte) ((exponent & 0x0F) << 4);
-                break;
-
-            case FORMAT_FLOAT:
-                mantissa = intToSignedBits(mantissa, 24);
-                exponent = intToSignedBits(exponent, 8);
-                mValue[offset++] = (byte) (mantissa & 0xFF);
-                mValue[offset++] = (byte) ((mantissa >> 8) & 0xFF);
-                mValue[offset++] = (byte) ((mantissa >> 16) & 0xFF);
-                mValue[offset] += (byte) (exponent & 0xFF);
-                break;
-
-            default:
-                return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Set the locally stored value of this characteristic.
-     * <p>See {@link #setValue(byte[])} for details.
-     *
-     * @param value New value for this characteristic
-     * @return true if the locally stored value has been set
-     *
-     * @deprecated Pass the characteristic value directly into
-     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
-     */
-    @Deprecated
-    public boolean setValue(String value) {
-        mValue = value.getBytes();
-        return true;
-    }
-
-    /**
-     * Returns the size of a give value type.
-     */
-    private int getTypeLen(int formatType) {
-        return formatType & 0xF;
-    }
-
-    /**
-     * Convert a signed byte to an unsigned int.
-     */
-    private int unsignedByteToInt(byte b) {
-        return b & 0xFF;
-    }
-
-    /**
-     * Convert signed bytes to a 16-bit unsigned int.
-     */
-    private int unsignedBytesToInt(byte b0, byte b1) {
-        return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8));
-    }
-
-    /**
-     * Convert signed bytes to a 32-bit unsigned int.
-     */
-    private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) {
-        return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8))
-                + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24);
-    }
-
-    /**
-     * Convert signed bytes to a 16-bit short float value.
-     */
-    private float bytesToFloat(byte b0, byte b1) {
-        int mantissa = unsignedToSigned(unsignedByteToInt(b0)
-                + ((unsignedByteToInt(b1) & 0x0F) << 8), 12);
-        int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4);
-        return (float) (mantissa * Math.pow(10, exponent));
-    }
-
-    /**
-     * Convert signed bytes to a 32-bit short float value.
-     */
-    private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) {
-        int mantissa = unsignedToSigned(unsignedByteToInt(b0)
-                + (unsignedByteToInt(b1) << 8)
-                + (unsignedByteToInt(b2) << 16), 24);
-        return (float) (mantissa * Math.pow(10, b3));
-    }
-
-    /**
-     * Convert an unsigned integer value to a two's-complement encoded
-     * signed value.
-     */
-    private int unsignedToSigned(int unsigned, int size) {
-        if ((unsigned & (1 << size - 1)) != 0) {
-            unsigned = -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1)));
-        }
-        return unsigned;
-    }
-
-    /**
-     * Convert an integer into the signed bits of a given length.
-     */
-    private int intToSignedBits(int i, int size) {
-        if (i < 0) {
-            i = (1 << size - 1) + (i & ((1 << size - 1) - 1));
-        }
-        return i;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
deleted file mode 100644
index a35d5b9..0000000
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Descriptor
- *
- * <p> GATT Descriptors contain additional information and attributes of a GATT
- * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe
- * the characteristic's features or to control certain behaviours of the characteristic.
- */
-public class BluetoothGattDescriptor implements Parcelable {
-
-    /**
-     * Value used to enable notification for a client configuration descriptor
-     */
-    public static final byte[] ENABLE_NOTIFICATION_VALUE = {0x01, 0x00};
-
-    /**
-     * Value used to enable indication for a client configuration descriptor
-     */
-    public static final byte[] ENABLE_INDICATION_VALUE = {0x02, 0x00};
-
-    /**
-     * Value used to disable notifications or indicatinos
-     */
-    public static final byte[] DISABLE_NOTIFICATION_VALUE = {0x00, 0x00};
-
-    /**
-     * Descriptor read permission
-     */
-    public static final int PERMISSION_READ = 0x01;
-
-    /**
-     * Descriptor permission: Allow encrypted read operations
-     */
-    public static final int PERMISSION_READ_ENCRYPTED = 0x02;
-
-    /**
-     * Descriptor permission: Allow reading with person-in-the-middle protection
-     */
-    public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
-
-    /**
-     * Descriptor write permission
-     */
-    public static final int PERMISSION_WRITE = 0x10;
-
-    /**
-     * Descriptor permission: Allow encrypted writes
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
-
-    /**
-     * Descriptor permission: Allow encrypted writes with person-in-the-middle
-     * protection
-     */
-    public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
-
-    /**
-     * Descriptor permission: Allow signed write operations
-     */
-    public static final int PERMISSION_WRITE_SIGNED = 0x80;
-
-    /**
-     * Descriptor permission: Allow signed write operations with
-     * person-in-the-middle protection
-     */
-    public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
-
-    /**
-     * The UUID of this descriptor.
-     *
-     * @hide
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this descriptor.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected int mInstance;
-
-    /**
-     * Permissions for this descriptor
-     *
-     * @hide
-     */
-    protected int mPermissions;
-
-    /**
-     * Back-reference to the characteristic this descriptor belongs to.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected BluetoothGattCharacteristic mCharacteristic;
-
-    /**
-     * The value for this descriptor.
-     *
-     * @hide
-     */
-    protected byte[] mValue;
-
-    /**
-     * Create a new BluetoothGattDescriptor.
-     *
-     * @param uuid The UUID for this descriptor
-     * @param permissions Permissions for this descriptor
-     */
-    public BluetoothGattDescriptor(UUID uuid, int permissions) {
-        initDescriptor(null, uuid, 0, permissions);
-    }
-
-    /**
-     * Create a new BluetoothGattDescriptor.
-     *
-     * @param characteristic The characteristic this descriptor belongs to
-     * @param uuid The UUID for this descriptor
-     * @param permissions Permissions for this descriptor
-     */
-    /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
-            int instance, int permissions) {
-        initDescriptor(characteristic, uuid, instance, permissions);
-    }
-
-    /**
-     * @hide
-     */
-    public BluetoothGattDescriptor(UUID uuid, int instance, int permissions) {
-        initDescriptor(null, uuid, instance, permissions);
-    }
-
-    private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
-            int instance, int permissions) {
-        mCharacteristic = characteristic;
-        mUuid = uuid;
-        mInstance = instance;
-        mPermissions = permissions;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstance);
-        out.writeInt(mPermissions);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattDescriptor> CREATOR =
-            new Parcelable.Creator<BluetoothGattDescriptor>() {
-        public BluetoothGattDescriptor createFromParcel(Parcel in) {
-            return new BluetoothGattDescriptor(in);
-        }
-
-        public BluetoothGattDescriptor[] newArray(int size) {
-            return new BluetoothGattDescriptor[size];
-        }
-    };
-
-    private BluetoothGattDescriptor(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
-        mInstance = in.readInt();
-        mPermissions = in.readInt();
-    }
-
-    /**
-     * Returns the characteristic this descriptor belongs to.
-     *
-     * @return The characteristic.
-     */
-    public BluetoothGattCharacteristic getCharacteristic() {
-        return mCharacteristic;
-    }
-
-    /**
-     * Set the back-reference to the associated characteristic
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
-        mCharacteristic = characteristic;
-    }
-
-    /**
-     * Returns the UUID of this descriptor.
-     *
-     * @return UUID of this descriptor
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this descriptor.
-     *
-     * <p>If a remote device offers multiple descriptors with the same UUID,
-     * the instance ID is used to distuinguish between descriptors.
-     *
-     * @return Instance ID of this descriptor
-     * @hide
-     */
-    public int getInstanceId() {
-        return mInstance;
-    }
-
-    /**
-     * Force the instance ID.
-     *
-     * @hide
-     */
-    public void setInstanceId(int instanceId) {
-        mInstance = instanceId;
-    }
-
-    /**
-     * Returns the permissions for this descriptor.
-     *
-     * @return Permissions of this descriptor
-     */
-    public int getPermissions() {
-        return mPermissions;
-    }
-
-    /**
-     * Returns the stored value for this descriptor
-     *
-     * <p>This function returns the stored value for this descriptor as
-     * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached
-     * value of the descriptor is updated as a result of a descriptor read
-     * operation.
-     *
-     * @return Cached value of the descriptor
-     *
-     * @deprecated  Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead
-     */
-    @Deprecated
-    public byte[] getValue() {
-        return mValue;
-    }
-
-    /**
-     * Updates the locally stored value of this descriptor.
-     *
-     * <p>This function modifies the locally stored cached value of this
-     * descriptor. To send the value to the remote device, call
-     * {@link BluetoothGatt#writeDescriptor} to send the value to the
-     * remote device.
-     *
-     * @param value New value for this descriptor
-     * @return true if the locally stored value has been set, false if the requested value could not
-     * be stored locally.
-     *
-     * @deprecated Pass the descriptor value directly into
-     * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}
-     */
-    @Deprecated
-    public boolean setValue(byte[] value) {
-        mValue = value;
-        return true;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.java b/core/java/android/bluetooth/BluetoothGattIncludedService.java
deleted file mode 100644
index 5580619..0000000
--- a/core/java/android/bluetooth/BluetoothGattIncludedService.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Included Service
- *
- * @hide
- */
-public class BluetoothGattIncludedService implements Parcelable {
-
-    /**
-     * The UUID of this service.
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this service.
-     */
-    protected int mInstanceId;
-
-    /**
-     * Service type (Primary/Secondary).
-     */
-    protected int mServiceType;
-
-    /**
-     * Create a new BluetoothGattIncludedService
-     */
-    public BluetoothGattIncludedService(UUID uuid, int instanceId, int serviceType) {
-        mUuid = uuid;
-        mInstanceId = instanceId;
-        mServiceType = serviceType;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstanceId);
-        out.writeInt(mServiceType);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattIncludedService> CREATOR =
-            new Parcelable.Creator<BluetoothGattIncludedService>() {
-        public BluetoothGattIncludedService createFromParcel(Parcel in) {
-            return new BluetoothGattIncludedService(in);
-        }
-
-        public BluetoothGattIncludedService[] newArray(int size) {
-            return new BluetoothGattIncludedService[size];
-        }
-    };
-
-    private BluetoothGattIncludedService(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
-        mInstanceId = in.readInt();
-        mServiceType = in.readInt();
-    }
-
-    /**
-     * Returns the UUID of this service
-     *
-     * @return UUID of this service
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this service
-     *
-     * <p>If a remote device offers multiple services with the same UUID
-     * (ex. multiple battery services for different batteries), the instance
-     * ID is used to distuinguish services.
-     *
-     * @return Instance ID of this service
-     */
-    public int getInstanceId() {
-        return mInstanceId;
-    }
-
-    /**
-     * Get the type of this service (primary/secondary)
-     */
-    public int getType() {
-        return mServiceType;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
deleted file mode 100644
index 08e0178..0000000
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ /dev/null
@@ -1,954 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Public API for the Bluetooth GATT Profile server role.
- *
- * <p>This class provides Bluetooth GATT server role functionality,
- * allowing applications to create Bluetooth Smart services and
- * characteristics.
- *
- * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
- * via IPC.  Use {@link BluetoothManager#openGattServer} to get an instance
- * of this class.
- */
-public final class BluetoothGattServer implements BluetoothProfile {
-    private static final String TAG = "BluetoothGattServer";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private final IBluetoothGatt mService;
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-
-    private BluetoothGattServerCallback mCallback;
-
-    private Object mServerIfLock = new Object();
-    private int mServerIf;
-    private int mTransport;
-    private BluetoothGattService mPendingService;
-    private List<BluetoothGattService> mServices;
-
-    private static final int CALLBACK_REG_TIMEOUT = 10000;
-
-    /**
-     * Bluetooth GATT interface callbacks
-     */
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
-            new IBluetoothGattServerCallback.Stub() {
-                /**
-                 * Application interface registered - app is ready to go
-                 * @hide
-                 */
-                @Override
-                public void onServerRegistered(int status, int serverIf) {
-                    if (DBG) {
-                        Log.d(TAG, "onServerRegistered() - status=" + status
-                                + " serverIf=" + serverIf);
-                    }
-                    synchronized (mServerIfLock) {
-                        if (mCallback != null) {
-                            mServerIf = serverIf;
-                            mServerIfLock.notify();
-                        } else {
-                            // registration timeout
-                            Log.e(TAG, "onServerRegistered: mCallback is null");
-                        }
-                    }
-                }
-
-                /**
-                 * Server connection state changed
-                 * @hide
-                 */
-                @Override
-                public void onServerConnectionState(int status, int serverIf,
-                        boolean connected, String address) {
-                    if (DBG) {
-                        Log.d(TAG, "onServerConnectionState() - status=" + status
-                                + " serverIf=" + serverIf + " device=" + address);
-                    }
-                    try {
-                        mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
-                                connected ? BluetoothProfile.STATE_CONNECTED :
-                                        BluetoothProfile.STATE_DISCONNECTED);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Service has been added
-                 * @hide
-                 */
-                @Override
-                public void onServiceAdded(int status, BluetoothGattService service) {
-                    if (DBG) {
-                        Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId()
-                                + " uuid=" + service.getUuid() + " status=" + status);
-                    }
-
-                    if (mPendingService == null) {
-                        return;
-                    }
-
-                    BluetoothGattService tmp = mPendingService;
-                    mPendingService = null;
-
-                    // Rewrite newly assigned handles to existing service.
-                    tmp.setInstanceId(service.getInstanceId());
-                    List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics();
-                    List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics();
-                    for (int i = 0; i < svc_chars.size(); i++) {
-                        BluetoothGattCharacteristic temp_char = temp_chars.get(i);
-                        BluetoothGattCharacteristic svc_char = svc_chars.get(i);
-
-                        temp_char.setInstanceId(svc_char.getInstanceId());
-
-                        List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors();
-                        List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors();
-                        for (int j = 0; j < svc_descs.size(); j++) {
-                            temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId());
-                        }
-                    }
-
-                    mServices.add(tmp);
-
-                    try {
-                        mCallback.onServiceAdded((int) status, tmp);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Remote client characteristic read request.
-                 * @hide
-                 */
-                @Override
-                public void onCharacteristicReadRequest(String address, int transId,
-                        int offset, boolean isLong, int handle) {
-                    if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
-                    if (characteristic == null) {
-                        Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onCharacteristicReadRequest(device, transId, offset,
-                                characteristic);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Remote client descriptor read request.
-                 * @hide
-                 */
-                @Override
-                public void onDescriptorReadRequest(String address, int transId,
-                        int offset, boolean isLong, int handle) {
-                    if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
-                    if (descriptor == null) {
-                        Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Remote client characteristic write request.
-                 * @hide
-                 */
-                @Override
-                public void onCharacteristicWriteRequest(String address, int transId,
-                        int offset, int length, boolean isPrep, boolean needRsp,
-                        int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
-                    if (characteristic == null) {
-                        Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
-                                isPrep, needRsp, offset, value);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-
-                }
-
-                /**
-                 * Remote client descriptor write request.
-                 * @hide
-                 */
-                @Override
-                public void onDescriptorWriteRequest(String address, int transId, int offset,
-                        int length, boolean isPrep, boolean needRsp, int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle);
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
-                    if (descriptor == null) {
-                        Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle);
-                        return;
-                    }
-
-                    try {
-                        mCallback.onDescriptorWriteRequest(device, transId, descriptor,
-                                isPrep, needRsp, offset, value);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * Execute pending writes.
-                 * @hide
-                 */
-                @Override
-                public void onExecuteWrite(String address, int transId,
-                        boolean execWrite) {
-                    if (DBG) {
-                        Log.d(TAG, "onExecuteWrite() - "
-                                + "device=" + address + ", transId=" + transId
-                                + "execWrite=" + execWrite);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onExecuteWrite(device, transId, execWrite);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception in callback", ex);
-                    }
-                }
-
-                /**
-                 * A notification/indication has been sent.
-                 * @hide
-                 */
-                @Override
-                public void onNotificationSent(String address, int status) {
-                    if (VDBG) {
-                        Log.d(TAG, "onNotificationSent() - "
-                                + "device=" + address + ", status=" + status);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onNotificationSent(device, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * The MTU for a connection has changed
-                 * @hide
-                 */
-                @Override
-                public void onMtuChanged(String address, int mtu) {
-                    if (DBG) {
-                        Log.d(TAG, "onMtuChanged() - "
-                                + "device=" + address + ", mtu=" + mtu);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onMtuChanged(device, mtu);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * The PHY for a connection was updated
-                 * @hide
-                 */
-                @Override
-                public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG,
-                                "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
-                                        + ", rxPHy=" + rxPhy);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * The PHY for a connection was read
-                 * @hide
-                 */
-                @Override
-                public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
-                    if (DBG) {
-                        Log.d(TAG,
-                                "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
-                                        + ", rxPHy=" + rxPhy);
-                    }
-
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onPhyRead(device, txPhy, rxPhy, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-                /**
-                 * Callback invoked when the given connection is updated
-                 * @hide
-                 */
-                @Override
-                public void onConnectionUpdated(String address, int interval, int latency,
-                        int timeout, int status) {
-                    if (DBG) {
-                        Log.d(TAG, "onConnectionUpdated() - Device=" + address
-                                + " interval=" + interval + " latency=" + latency
-                                + " timeout=" + timeout + " status=" + status);
-                    }
-                    BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                    if (device == null) return;
-
-                    try {
-                        mCallback.onConnectionUpdated(device, interval, latency,
-                                timeout, status);
-                    } catch (Exception ex) {
-                        Log.w(TAG, "Unhandled exception: " + ex);
-                    }
-                }
-
-            };
-
-    /**
-     * Create a BluetoothGattServer proxy object.
-     */
-    /* package */ BluetoothGattServer(IBluetoothGatt iGatt, int transport,
-            BluetoothAdapter adapter) {
-        mService = iGatt;
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mCallback = null;
-        mServerIf = 0;
-        mTransport = transport;
-        mServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * Returns a characteristic with given handle.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                if (charac.getInstanceId() == handle) {
-                    return charac;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns a descriptor with given handle.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
-        for (BluetoothGattService svc : mServices) {
-            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
-                for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
-                    if (desc.getInstanceId() == handle) {
-                        return desc;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Close this GATT server instance.
-     *
-     * Application should call this method as early as possible after it is done with
-     * this GATT server.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void close() {
-        if (DBG) Log.d(TAG, "close()");
-        unregisterCallback();
-    }
-
-    /**
-     * Register an application callback to start using GattServer.
-     *
-     * <p>This is an asynchronous call. The callback is used to notify
-     * success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @return true, the callback will be called to notify success or failure, false on immediate
-     * error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
-        return registerCallback(callback, false);
-    }
-
-    /**
-     * Register an application callback to start using GattServer.
-     *
-     * <p>This is an asynchronous call. The callback is used to notify
-     * success or failure if the function returns true.
-     *
-     * @param callback GATT callback handler that will receive asynchronous callbacks.
-     * @param eatt_support indicates if server can use eatt
-     * @return true, the callback will be called to notify success or failure, false on immediate
-     * error
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback,
-                                         boolean eatt_support) {
-        if (DBG) Log.d(TAG, "registerCallback()");
-        if (mService == null) {
-            Log.e(TAG, "GATT service not available");
-            return false;
-        }
-        UUID uuid = UUID.randomUUID();
-        if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
-
-        synchronized (mServerIfLock) {
-            if (mCallback != null) {
-                Log.e(TAG, "App can register callback only once");
-                return false;
-            }
-
-            mCallback = callback;
-            try {
-                mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback,
-                        eatt_support, mAttributionSource);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-                mCallback = null;
-                return false;
-            }
-
-            try {
-                mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
-            } catch (InterruptedException e) {
-                Log.e(TAG, "" + e);
-                mCallback = null;
-            }
-
-            if (mServerIf == 0) {
-                mCallback = null;
-                return false;
-            } else {
-                return true;
-            }
-        }
-    }
-
-    /**
-     * Unregister the current application and callbacks.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void unregisterCallback() {
-        if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mCallback = null;
-            mService.unregisterServer(mServerIf, mAttributionSource);
-            mServerIf = 0;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Returns a service by UUID, instance and type.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
-        for (BluetoothGattService svc : mServices) {
-            if (svc.getType() == type
-                    && svc.getInstanceId() == instanceId
-                    && svc.getUuid().equals(uuid)) {
-                return svc;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Initiate a connection to a Bluetooth GATT capable device.
-     *
-     * <p>The connection may not be established right away, but will be
-     * completed when the remote device is available. A
-     * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
-     * invoked when the connection state changes as a result of this function.
-     *
-     * <p>The autoConnect parameter determines whether to actively connect to
-     * the remote device, or rather passively scan and finalize the connection
-     * when the remote device is in range/available. Generally, the first ever
-     * connection to a device should be direct (autoConnect set to false) and
-     * subsequent connections to known devices should be invoked with the
-     * autoConnect parameter set to true.
-     *
-     * @param autoConnect Whether to directly connect to the remote device (false) or to
-     * automatically connect as soon as the remote device becomes available (true).
-     * @return true, if the connection attempt was initiated successfully
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(BluetoothDevice device, boolean autoConnect) {
-        if (DBG) {
-            Log.d(TAG,
-                    "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
-        }
-        if (mService == null || mServerIf == 0) return false;
-
-        try {
-            // autoConnect is inverse of "isDirect"
-            mService.serverConnect(
-                    mServerIf, device.getAddress(), !autoConnect, mTransport, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Disconnects an established connection, or cancels a connection attempt
-     * currently in progress.
-     *
-     * @param device Remote device
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void cancelConnection(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Set the preferred connection PHY for this app. Please note that this is just a
-     * recommendation, whether the PHY change will happen depends on other applications peferences,
-     * local and remote controller capabilities. Controller can override these settings. <p> {@link
-     * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if
-     * no PHY change happens. It is also triggered when remote device updates the PHY.
-     *
-     * @param device The remote device to send this response to
-     * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
-     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
-     * BluetoothDevice#PHY_LE_CODED_MASK}.
-     * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
-     * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
-     * {@link BluetoothDevice#PHY_OPTION_S8}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
-        try {
-            mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
-                    phyOptions, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
-     * in {@link BluetoothGattServerCallback#onPhyRead}
-     *
-     * @param device The remote device to send this response to
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void readPhy(BluetoothDevice device) {
-        try {
-            mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Send a response to a read or write request to a remote device.
-     *
-     * <p>This function must be invoked in when a remote read/write request
-     * is received by one of these callback methods:
-     *
-     * <ul>
-     * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
-     * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
-     * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
-     * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
-     * </ul>
-     *
-     * @param device The remote device to send this response to
-     * @param requestId The ID of the request that was received with the callback
-     * @param status The status of the request to be sent to the remote devices
-     * @param offset Value offset for partial read/write response
-     * @param value The value of the attribute that was read/written (optional)
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendResponse(BluetoothDevice device, int requestId,
-            int status, int offset, byte[] value) {
-        if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
-        if (mService == null || mServerIf == 0) return false;
-
-        try {
-            mService.sendResponse(mServerIf, device.getAddress(), requestId,
-                    status, offset, value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Send a notification or indication that a local characteristic has been
-     * updated.
-     *
-     * <p>A notification or indication is sent to the remote device to signal
-     * that the characteristic has been updated. This function should be invoked
-     * for every client that requests notifications/indications by writing
-     * to the "Client Configuration" descriptor for the given characteristic.
-     *
-     * @param device The remote device to receive the notification/indication
-     * @param characteristic The local characteristic that has been updated
-     * @param confirm true to request confirmation from the client (indication), false to send a
-     * notification
-     * @return true, if the notification has been triggered successfully
-     * @throws IllegalArgumentException
-     *
-     * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice,
-     * BluetoothGattCharacteristic, boolean, byte[])}  as this is not memory safe.
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean notifyCharacteristicChanged(BluetoothDevice device,
-            BluetoothGattCharacteristic characteristic, boolean confirm) {
-        return notifyCharacteristicChanged(device, characteristic, confirm,
-                characteristic.getValue()) == BluetoothStatusCodes.SUCCESS;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
-            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
-            BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
-            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
-            BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
-            BluetoothStatusCodes.ERROR_UNKNOWN
-    })
-    public @interface NotifyCharacteristicReturnValues{}
-
-    /**
-     * Send a notification or indication that a local characteristic has been
-     * updated.
-     *
-     * <p>A notification or indication is sent to the remote device to signal
-     * that the characteristic has been updated. This function should be invoked
-     * for every client that requests notifications/indications by writing
-     * to the "Client Configuration" descriptor for the given characteristic.
-     *
-     * @param device the remote device to receive the notification/indication
-     * @param characteristic the local characteristic that has been updated
-     * @param confirm {@code true} to request confirmation from the client (indication) or
-     * {@code false} to send a notification
-     * @param value the characteristic value
-     * @return whether the notification has been triggered successfully
-     * @throws IllegalArgumentException if the characteristic value or service is null
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @NotifyCharacteristicReturnValues
-    public int notifyCharacteristicChanged(@NonNull BluetoothDevice device,
-            @NonNull BluetoothGattCharacteristic characteristic, boolean confirm,
-            @NonNull byte[] value) {
-        if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
-        if (mService == null || mServerIf == 0) {
-            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
-        }
-
-        if (characteristic == null) {
-            throw new IllegalArgumentException("characteristic must not be null");
-        }
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        BluetoothGattService service = characteristic.getService();
-        if (service == null) {
-            throw new IllegalArgumentException("Characteristic must have a non-null service");
-        }
-        if (value == null) {
-            throw new IllegalArgumentException("Characteristic value must not be null");
-        }
-
-        try {
-            return mService.sendNotification(mServerIf, device.getAddress(),
-                    characteristic.getInstanceId(), confirm,
-                    value, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Add a service to the list of services to be hosted.
-     *
-     * <p>Once a service has been addded to the list, the service and its
-     * included characteristics will be provided by the local device.
-     *
-     * <p>If the local device has already exposed services when this function
-     * is called, a service update notification will be sent to all clients.
-     *
-     * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
-     * whether this service has been added successfully. Do not add another service
-     * before this callback.
-     *
-     * @param service Service to be added to the list of services provided by this device.
-     * @return true, if the request to add service has been initiated
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean addService(BluetoothGattService service) {
-        if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
-        if (mService == null || mServerIf == 0) return false;
-
-        mPendingService = service;
-
-        try {
-            mService.addService(mServerIf, service, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Removes a service from the list of services to be provided.
-     *
-     * @param service Service to be removed.
-     * @return true, if the service has been removed
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean removeService(BluetoothGattService service) {
-        if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
-        if (mService == null || mServerIf == 0) return false;
-
-        BluetoothGattService intService = getService(service.getUuid(),
-                service.getInstanceId(), service.getType());
-        if (intService == null) return false;
-
-        try {
-            mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource);
-            mServices.remove(intService);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Remove all services from the list of provided services.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void clearServices() {
-        if (DBG) Log.d(TAG, "clearServices()");
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mService.clearServices(mServerIf, mAttributionSource);
-            mServices.clear();
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-    }
-
-    /**
-     * Returns a list of GATT services offered by this device.
-     *
-     * <p>An application must call {@link #addService} to add a serice to the
-     * list of services offered by this device.
-     *
-     * @return List of services. Returns an empty list if no services have been added yet.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public List<BluetoothGattService> getServices() {
-        return mServices;
-    }
-
-    /**
-     * Returns a {@link BluetoothGattService} from the list of services offered
-     * by this device.
-     *
-     * <p>If multiple instances of the same service (as identified by UUID)
-     * exist, the first instance of the service is returned.
-     *
-     * @param uuid UUID of the requested service
-     * @return BluetoothGattService if supported, or null if the requested service is not offered by
-     * this device.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresNoPermission
-    public BluetoothGattService getService(UUID uuid) {
-        for (BluetoothGattService service : mServices) {
-            if (service.getUuid().equals(uuid)) {
-                return service;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    public int getConnectionState(BluetoothDevice device) {
-        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
-    }
-
-    /**
-     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
-     * with {@link BluetoothProfile#GATT} as argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    public List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getConnectedDevices instead.");
-    }
-
-    /**
-     * Not supported - please use
-     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
-     * with {@link BluetoothProfile#GATT} as first argument
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    @RequiresNoPermission
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        throw new UnsupportedOperationException(
-                "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
deleted file mode 100644
index 0ead5f5..0000000
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * This abstract class is used to implement {@link BluetoothGattServer} callbacks.
- */
-public abstract class BluetoothGattServerCallback {
-
-    /**
-     * Callback indicating when a remote device has been connected or disconnected.
-     *
-     * @param device Remote device that has been connected or disconnected.
-     * @param status Status of the connect or disconnect operation.
-     * @param newState Returns the new connection state. Can be one of {@link
-     * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED}
-     */
-    public void onConnectionStateChange(BluetoothDevice device, int status,
-            int newState) {
-    }
-
-    /**
-     * Indicates whether a local service has been added successfully.
-     *
-     * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service was added
-     * successfully.
-     * @param service The service that has been added
-     */
-    public void onServiceAdded(int status, BluetoothGattService service) {
-    }
-
-    /**
-     * A remote client has requested to read a local characteristic.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the read operation
-     * @param requestId The Id of the request
-     * @param offset Offset into the value of the characteristic
-     * @param characteristic Characteristic to be read
-     */
-    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
-            int offset, BluetoothGattCharacteristic characteristic) {
-    }
-
-    /**
-     * A remote client has requested to write to a local characteristic.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the write operation
-     * @param requestId The Id of the request
-     * @param characteristic Characteristic to be written to.
-     * @param preparedWrite true, if this write operation should be queued for later execution.
-     * @param responseNeeded true, if the remote device requires a response
-     * @param offset The offset given for the value
-     * @param value The value the client wants to assign to the characteristic
-     */
-    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
-            BluetoothGattCharacteristic characteristic,
-            boolean preparedWrite, boolean responseNeeded,
-            int offset, byte[] value) {
-    }
-
-    /**
-     * A remote client has requested to read a local descriptor.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the read operation
-     * @param requestId The Id of the request
-     * @param offset Offset into the value of the characteristic
-     * @param descriptor Descriptor to be read
-     */
-    public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
-            int offset, BluetoothGattDescriptor descriptor) {
-    }
-
-    /**
-     * A remote client has requested to write to a local descriptor.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the write operation
-     * @param requestId The Id of the request
-     * @param descriptor Descriptor to be written to.
-     * @param preparedWrite true, if this write operation should be queued for later execution.
-     * @param responseNeeded true, if the remote device requires a response
-     * @param offset The offset given for the value
-     * @param value The value the client wants to assign to the descriptor
-     */
-    public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
-            BluetoothGattDescriptor descriptor,
-            boolean preparedWrite, boolean responseNeeded,
-            int offset, byte[] value) {
-    }
-
-    /**
-     * Execute all pending write operations for this device.
-     *
-     * <p>An application must call {@link BluetoothGattServer#sendResponse}
-     * to complete the request.
-     *
-     * @param device The remote device that has requested the write operations
-     * @param requestId The Id of the request
-     * @param execute Whether the pending writes should be executed (true) or cancelled (false)
-     */
-    public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
-    }
-
-    /**
-     * Callback invoked when a notification or indication has been sent to
-     * a remote device.
-     *
-     * <p>When multiple notifications are to be sent, an application must
-     * wait for this callback to be received before sending additional
-     * notifications.
-     *
-     * @param device The remote device the notification has been sent to
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful
-     */
-    public void onNotificationSent(BluetoothDevice device, int status) {
-    }
-
-    /**
-     * Callback indicating the MTU for a given device connection has changed.
-     *
-     * <p>This callback will be invoked if a remote client has requested to change
-     * the MTU for a given connection.
-     *
-     * @param device The remote device that requested the MTU change
-     * @param mtu The new MTU size
-     */
-    public void onMtuChanged(BluetoothDevice device, int mtu) {
-    }
-
-    /**
-     * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result
-     * of remote device changing the PHY.
-     *
-     * @param device The remote device
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback triggered as result of {@link BluetoothGattServer#readPhy}
-     *
-     * @param device The remote device that requested the PHY read
-     * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link
-     * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
-     * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
-     */
-    public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
-    }
-
-    /**
-     * Callback indicating the connection parameters were updated.
-     *
-     * @param device The remote device involved
-     * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
-     * 6 (7.5ms) to 3200 (4000ms).
-     * @param latency Worker latency for the connection in number of connection events. Valid range
-     * is from 0 to 499
-     * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
-     * (0.1s) to 3200 (32s)
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated
-     * successfully
-     * @hide
-     */
-    public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout,
-            int status) {
-    }
-
-}
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
deleted file mode 100644
index f64d09f..0000000
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth;
-
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth GATT Service
- *
- * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic},
- * as well as referenced services.
- */
-public class BluetoothGattService implements Parcelable {
-
-    /**
-     * Primary service
-     */
-    public static final int SERVICE_TYPE_PRIMARY = 0;
-
-    /**
-     * Secondary service (included by primary services)
-     */
-    public static final int SERVICE_TYPE_SECONDARY = 1;
-
-
-    /**
-     * The remote device this service is associated with.
-     * This applies to client applications only.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    protected BluetoothDevice mDevice;
-
-    /**
-     * The UUID of this service.
-     *
-     * @hide
-     */
-    protected UUID mUuid;
-
-    /**
-     * Instance ID for this service.
-     *
-     * @hide
-     */
-    protected int mInstanceId;
-
-    /**
-     * Handle counter override (for conformance testing).
-     *
-     * @hide
-     */
-    protected int mHandles = 0;
-
-    /**
-     * Service type (Primary/Secondary).
-     *
-     * @hide
-     */
-    protected int mServiceType;
-
-    /**
-     * List of characteristics included in this service.
-     */
-    protected List<BluetoothGattCharacteristic> mCharacteristics;
-
-    /**
-     * List of included services for this service.
-     */
-    protected List<BluetoothGattService> mIncludedServices;
-
-    /**
-     * Whether the service uuid should be advertised.
-     */
-    private boolean mAdvertisePreferred;
-
-    /**
-     * Create a new BluetoothGattService.
-     *
-     * @param uuid The UUID for this service
-     * @param serviceType The type of this service,
-     * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY}
-     * or {@link BluetoothGattService#SERVICE_TYPE_SECONDARY}
-     */
-    public BluetoothGattService(UUID uuid, int serviceType) {
-        mDevice = null;
-        mUuid = uuid;
-        mInstanceId = 0;
-        mServiceType = serviceType;
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * Create a new BluetoothGattService
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid,
-            int instanceId, int serviceType) {
-        mDevice = device;
-        mUuid = uuid;
-        mInstanceId = instanceId;
-        mServiceType = serviceType;
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * Create a new BluetoothGattService
-     *
-     * @hide
-     */
-    public BluetoothGattService(UUID uuid, int instanceId, int serviceType) {
-        mDevice = null;
-        mUuid = uuid;
-        mInstanceId = instanceId;
-        mServiceType = serviceType;
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-    }
-
-    /**
-     * @hide
-     */
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeInt(mInstanceId);
-        out.writeInt(mServiceType);
-        out.writeTypedList(mCharacteristics);
-
-        ArrayList<BluetoothGattIncludedService> includedServices =
-                new ArrayList<BluetoothGattIncludedService>(mIncludedServices.size());
-        for (BluetoothGattService s : mIncludedServices) {
-            includedServices.add(new BluetoothGattIncludedService(s.getUuid(),
-                    s.getInstanceId(), s.getType()));
-        }
-        out.writeTypedList(includedServices);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattService> CREATOR =
-            new Parcelable.Creator<BluetoothGattService>() {
-        public BluetoothGattService createFromParcel(Parcel in) {
-            return new BluetoothGattService(in);
-        }
-
-        public BluetoothGattService[] newArray(int size) {
-            return new BluetoothGattService[size];
-        }
-    };
-
-    private BluetoothGattService(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
-        mInstanceId = in.readInt();
-        mServiceType = in.readInt();
-
-        mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
-
-        ArrayList<BluetoothGattCharacteristic> chrcs =
-                in.createTypedArrayList(BluetoothGattCharacteristic.CREATOR);
-        if (chrcs != null) {
-            for (BluetoothGattCharacteristic chrc : chrcs) {
-                chrc.setService(this);
-                mCharacteristics.add(chrc);
-            }
-        }
-
-        mIncludedServices = new ArrayList<BluetoothGattService>();
-
-        ArrayList<BluetoothGattIncludedService> inclSvcs =
-                in.createTypedArrayList(BluetoothGattIncludedService.CREATOR);
-        if (chrcs != null) {
-            for (BluetoothGattIncludedService isvc : inclSvcs) {
-                mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(),
-                        isvc.getInstanceId(), isvc.getType()));
-            }
-        }
-    }
-
-    /**
-     * Returns the device associated with this service.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Returns the device associated with this service.
-     *
-     * @hide
-     */
-    /*package*/ void setDevice(BluetoothDevice device) {
-        mDevice = device;
-    }
-
-    /**
-     * Add an included service to this service.
-     *
-     * @param service The service to be added
-     * @return true, if the included service was added to the service
-     */
-    @RequiresLegacyBluetoothPermission
-    public boolean addService(BluetoothGattService service) {
-        mIncludedServices.add(service);
-        return true;
-    }
-
-    /**
-     * Add a characteristic to this service.
-     *
-     * @param characteristic The characteristics to be added
-     * @return true, if the characteristic was added to the service
-     */
-    @RequiresLegacyBluetoothPermission
-    public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) {
-        mCharacteristics.add(characteristic);
-        characteristic.setService(this);
-        return true;
-    }
-
-    /**
-     * Get characteristic by UUID and instanceId.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) {
-        for (BluetoothGattCharacteristic characteristic : mCharacteristics) {
-            if (uuid.equals(characteristic.getUuid())
-                    && characteristic.getInstanceId() == instanceId) {
-                return characteristic;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Force the instance ID.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void setInstanceId(int instanceId) {
-        mInstanceId = instanceId;
-    }
-
-    /**
-     * Get the handle count override (conformance testing.
-     *
-     * @hide
-     */
-    /*package*/ int getHandles() {
-        return mHandles;
-    }
-
-    /**
-     * Force the number of handles to reserve for this service.
-     * This is needed for conformance testing only.
-     *
-     * @hide
-     */
-    public void setHandles(int handles) {
-        mHandles = handles;
-    }
-
-    /**
-     * Add an included service to the internal map.
-     *
-     * @hide
-     */
-    public void addIncludedService(BluetoothGattService includedService) {
-        mIncludedServices.add(includedService);
-    }
-
-    /**
-     * Returns the UUID of this service
-     *
-     * @return UUID of this service
-     */
-    public UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns the instance ID for this service
-     *
-     * <p>If a remote device offers multiple services with the same UUID
-     * (ex. multiple battery services for different batteries), the instance
-     * ID is used to distuinguish services.
-     *
-     * @return Instance ID of this service
-     */
-    public int getInstanceId() {
-        return mInstanceId;
-    }
-
-    /**
-     * Get the type of this service (primary/secondary)
-     */
-    public int getType() {
-        return mServiceType;
-    }
-
-    /**
-     * Get the list of included GATT services for this service.
-     *
-     * @return List of included services or empty list if no included services were discovered.
-     */
-    public List<BluetoothGattService> getIncludedServices() {
-        return mIncludedServices;
-    }
-
-    /**
-     * Returns a list of characteristics included in this service.
-     *
-     * @return Characteristics included in this service
-     */
-    public List<BluetoothGattCharacteristic> getCharacteristics() {
-        return mCharacteristics;
-    }
-
-    /**
-     * Returns a characteristic with a given UUID out of the list of
-     * characteristics offered by this service.
-     *
-     * <p>This is a convenience function to allow access to a given characteristic
-     * without enumerating over the list returned by {@link #getCharacteristics}
-     * manually.
-     *
-     * <p>If a remote service offers multiple characteristics with the same
-     * UUID, the first instance of a characteristic with the given UUID
-     * is returned.
-     *
-     * @return GATT characteristic object or null if no characteristic with the given UUID was
-     * found.
-     */
-    public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
-        for (BluetoothGattCharacteristic characteristic : mCharacteristics) {
-            if (uuid.equals(characteristic.getUuid())) {
-                return characteristic;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns whether the uuid of the service should be advertised.
-     *
-     * @hide
-     */
-    public boolean isAdvertisePreferred() {
-        return mAdvertisePreferred;
-    }
-
-    /**
-     * Set whether the service uuid should be advertised.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void setAdvertisePreferred(boolean advertisePreferred) {
-        mAdvertisePreferred = advertisePreferred;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
deleted file mode 100644
index 2ed1eb4..0000000
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ /dev/null
@@ -1,1516 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Public API for controlling the Bluetooth Headset Service. This includes both
- * Bluetooth Headset and Handsfree (v1.5) profiles.
- *
- * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
- * Service via IPC.
- *
- * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHeadset proxy object. Use
- * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
- *
- * <p> Android only supports one connected Bluetooth Headset at a time.
- * Each method is protected with its appropriate permission.
- */
-public final class BluetoothHeadset implements BluetoothProfile {
-    private static final String TAG = "BluetoothHeadset";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Headset
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the change in the Audio Connection state of the
-     * HFP profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * Intent used to broadcast that the headset has posted a
-     * vendor-specific event.
-     *
-     * <p>This intent will have 4 extras and 1 category.
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
-     * </li>
-     * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor
-     * specific command </li>
-     * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT
-     * command type which can be one of  {@link #AT_CMD_TYPE_READ},
-     * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET},
-     * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li>
-     * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command
-     * arguments. </li>
-     * </ul>
-     *
-     * <p> The category is the Company ID of the vendor defining the
-     * vendor-specific command. {@link BluetoothAssignedNumbers}
-     *
-     * For example, for Plantronics specific events
-     * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
-     *
-     * <p> For example, an AT+XEVENT=foo,3 will get translated into
-     * <ul>
-     * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li>
-     * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
-     * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
-     * </ul>
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
-            "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
-
-    /**
-     * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * intents that contains the name of the vendor-specific command.
-     */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
-
-    /**
-     * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * intents that contains the AT command type of the vendor-specific command.
-     */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
-
-    /**
-     * AT command type READ used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+VGM?. There are no arguments for this command type.
-     */
-    public static final int AT_CMD_TYPE_READ = 0;
-
-    /**
-     * AT command type TEST used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+VGM=?. There are no arguments for this command type.
-     */
-    public static final int AT_CMD_TYPE_TEST = 1;
-
-    /**
-     * AT command type SET used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+VGM=<args>.
-     */
-    public static final int AT_CMD_TYPE_SET = 2;
-
-    /**
-     * AT command type BASIC used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, ATD. Single character commands and everything following the
-     * character are arguments.
-     */
-    public static final int AT_CMD_TYPE_BASIC = 3;
-
-    /**
-     * AT command type ACTION used with
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
-     * For example, AT+CHUP. There are no arguments for action commands.
-     */
-    public static final int AT_CMD_TYPE_ACTION = 4;
-
-    /**
-     * A Parcelable String array extra field in
-     * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
-     * the arguments to the vendor-specific command.
-     */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
-
-    /**
-     * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * for the companyId
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
-            "android.bluetooth.headset.intent.category.companyid";
-
-    /**
-     * A vendor-specific command for unsolicited result code.
-     */
-    public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
-
-    /**
-     * A vendor-specific AT command
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
-
-    /**
-     * A vendor-specific AT command
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
-
-    /**
-     * Battery level indicator associated with
-     * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
-     *
-     * @hide
-     */
-    public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
-
-    /**
-     * A vendor-specific AT command
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
-
-    /**
-     * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
-     *
-     * @hide
-     */
-    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
-
-    /**
-     * Headset state when SCO audio is not connected.
-     * This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
-     */
-    public static final int STATE_AUDIO_DISCONNECTED = 10;
-
-    /**
-     * Headset state when SCO audio is connecting.
-     * This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
-     */
-    public static final int STATE_AUDIO_CONNECTING = 11;
-
-    /**
-     * Headset state when SCO audio is connected.
-     * This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
-     */
-    public static final int STATE_AUDIO_CONNECTED = 12;
-
-    /**
-     * Intent used to broadcast the headset's indicator status
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
-     * is supported by the headset ( as indicated by AT+BIND command in the SLC
-     * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
-     * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
-     * </ul>
-     * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
-     * are given an assigned number. Below shows the assigned number of Indicator added so far
-     * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
-     * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
-            "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
-
-    /**
-     * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
-     * intents that contains the assigned number of the headset indicator as defined by
-     * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
-     *
-     * @hide
-     */
-    public static final String EXTRA_HF_INDICATORS_IND_ID =
-            "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
-
-    /**
-     * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
-     * intents that contains the value of the Headset indicator that is being sent.
-     *
-     * @hide
-     */
-    public static final String EXTRA_HF_INDICATORS_IND_VALUE =
-            "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
-
-    private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
-    private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
-
-    private final CloseGuard mCloseGuard = new CloseGuard();
-
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothHeadset mService;
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        doUnbind();
-                    } else {
-                        doBind();
-                    }
-                }
-            };
-
-    /**
-     * Create a BluetoothHeadset proxy object.
-     */
-    /* package */ BluetoothHeadset(Context context, ServiceListener l, BluetoothAdapter adapter) {
-        mContext = context;
-        mServiceListener = l;
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-
-        // Preserve legacy compatibility where apps were depending on
-        // registerStateChangeCallback() performing a permissions check which
-        // has been relaxed in modern platform versions
-        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
-                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Need BLUETOOTH permission");
-        }
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-        mCloseGuard.open("close");
-    }
-
-    private boolean doBind() {
-        synchronized (mConnection) {
-            if (mService == null) {
-                if (VDBG) Log.d(TAG, "Binding service...");
-                try {
-                    return mAdapter.getBluetoothManager().bindBluetoothProfileService(
-                            BluetoothProfile.HEADSET, mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to bind HeadsetService", e);
-                }
-            }
-        }
-        return false;
-    }
-
-    private void doUnbind() {
-        synchronized (mConnection) {
-            if (mService != null) {
-                if (VDBG) Log.d(TAG, "Unbinding service...");
-                try {
-                    mAdapter.getBluetoothManager().unbindBluetoothProfileService(
-                            BluetoothProfile.HEADSET, mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to unbind HeadsetService", e);
-                } finally {
-                    mService = null;
-                }
-            }
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothHeadset will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     */
-    @UnsupportedAppUsage
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "", re);
-            }
-        }
-        mServiceListener = null;
-        doUnbind();
-        mCloseGuard.close();
-    }
-
-    /** {@hide} */
-    @Override
-    protected void finalize() throws Throwable {
-        mCloseGuard.warnIfOpen();
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> Currently, the system supports only 1 connection to the
-     * headset/handsfree profile. The API will automatically disconnect connected
-     * devices before connecting.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHeadset service = mService;
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHeadset service = mService;
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
-     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Checks whether the headset supports some form of noise reduction
-     *
-     * @param device Bluetooth device
-     * @return true if echo cancellation and/or noise reduction is supported, false otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
-        if (DBG) log("isNoiseReductionSupported()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isNoiseReductionSupported(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Checks whether the headset supports voice recognition
-     *
-     * @param device Bluetooth device
-     * @return true if voice recognition is supported, false otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
-        if (DBG) log("isVoiceRecognitionSupported()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isVoiceRecognitionSupported(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Start Bluetooth voice recognition. This methods sends the voice
-     * recognition AT command to the headset and establishes the
-     * audio connection.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
-     *
-     * <p> {@link #EXTRA_STATE} will transition from
-     * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
-     * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
-     * in case of failure to establish the audio connection.
-     *
-     * @param device Bluetooth headset
-     * @return false if there is no headset connected, or the connected headset doesn't support
-     * voice recognition, or voice recognition is already started, or audio channel is occupied,
-     * or on error, true otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean startVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("startVoiceRecognition()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.startVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Stop Bluetooth Voice Recognition mode, and shut down the
-     * Bluetooth audio path.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
-     *
-     * @param device Bluetooth headset
-     * @return false if there is no headset connected, or voice recognition has not started,
-     * or voice recognition has ended on this headset, or on error, true otherwise
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean stopVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("stopVoiceRecognition()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.stopVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if Bluetooth SCO audio is connected.
-     *
-     * @param device Bluetooth headset
-     * @return true if SCO is connected, false otherwise or on error
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isAudioConnected(BluetoothDevice device) {
-        if (VDBG) log("isAudioConnected()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isAudioConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Indicates if current platform supports voice dialing over bluetooth SCO.
-     *
-     * @return true if voice dialing over bluetooth is supported, false otherwise.
-     * @hide
-     */
-    public static boolean isBluetoothVoiceDialingEnabled(Context context) {
-        return context.getResources().getBoolean(
-                com.android.internal.R.bool.config_bluetooth_sco_off_call);
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-            BluetoothHeadset.STATE_AUDIO_CONNECTING,
-            BluetoothHeadset.STATE_AUDIO_CONNECTED,
-            BluetoothStatusCodes.ERROR_TIMEOUT
-    })
-    public @interface GetAudioStateReturnValues {}
-
-    /**
-     * Get the current audio state of the Headset.
-     *
-     * @param device is the Bluetooth device for which the audio state is being queried
-     * @return the audio state of the device or an error code
-     * @throws IllegalArgumentException if the device is null
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @GetAudioStateReturnValues int getAudioState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getAudioState");
-        if (device == null) {
-            throw new IllegalArgumentException("device cannot be null");
-        }
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (!isDisabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getAudioState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-                throw e.rethrowFromSystemServer();
-            } catch (TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-                return BluetoothStatusCodes.ERROR_TIMEOUT;
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
-     * audio to the HF unless explicitly told to.
-     * This method should be used in cases where the SCO channel is shared between multiple profiles
-     * and must be delegated by a source knowledgeable
-     * Note: This is an internal function and shouldn't be exposed
-     *
-     * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setAudioRouteAllowed(boolean allowed) {
-        if (VDBG) log("setAudioRouteAllowed");
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setAudioRouteAllowed(allowed, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
-     * Note: This is an internal function and shouldn't be exposed
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getAudioRouteAllowed() {
-        if (VDBG) log("getAudioRouteAllowed");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getAudioRouteAllowed(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Force SCO audio to be opened regardless any other restrictions
-     *
-     * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio
-     * False to use SCO audio in normal manner
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setForceScoAudio(boolean forced) {
-        if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setForceScoAudio(forced, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
-            BluetoothStatusCodes.ERROR_TIMEOUT,
-            BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED,
-            BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES,
-            BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
-            BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED,
-            BluetoothStatusCodes.ERROR_CALL_ACTIVE,
-            BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED
-    })
-    public @interface ConnectAudioReturnValues {}
-
-    /**
-     * Initiates a connection of SCO audio to the current active HFP device. The active HFP device
-     * can be identified with {@link BluetoothAdapter#getActiveDevices(int)}.
- * <p>
-     * If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent
-     * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted twice. First with {@link #EXTRA_STATE}
-     * set to {@link #STATE_AUDIO_CONNECTING}. This will be followed by a broadcast with
-     * {@link #EXTRA_STATE} set to either {@link #STATE_AUDIO_CONNECTED} if the audio connection is
-     * established or {@link #STATE_AUDIO_DISCONNECTED} if there was a failure in establishing the
-     * audio connection.
-     *
-     * @return whether the connection was successfully initiated or an error code on failure
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectAudioReturnValues int connectAudio() {
-        if (VDBG) log("connectAudio()");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.connectAudio(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-                throw e.rethrowFromSystemServer();
-            } catch (TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-                return BluetoothStatusCodes.ERROR_TIMEOUT;
-            }
-        }
-        return defaultValue;
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            BluetoothStatusCodes.SUCCESS,
-            BluetoothStatusCodes.ERROR_UNKNOWN,
-            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
-            BluetoothStatusCodes.ERROR_TIMEOUT,
-            BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED,
-            BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED
-    })
-    public @interface DisconnectAudioReturnValues {}
-
-    /**
-     * Initiates a disconnection of HFP SCO audio from actively connected devices. It also tears
-     * down voice recognition or virtual voice call, if any exists.
-     *
-     * <p> If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent
-     * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted with {@link #EXTRA_STATE} set to
-     * {@link #STATE_AUDIO_DISCONNECTED}.
-     *
-     * @return whether the disconnection was initiated successfully or an error code on failure
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @DisconnectAudioReturnValues int disconnectAudio() {
-        if (VDBG) log("disconnectAudio()");
-        final IBluetoothHeadset service = mService;
-        final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.disconnectAudio(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-                throw e.rethrowFromSystemServer();
-            } catch (TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-                return BluetoothStatusCodes.ERROR_TIMEOUT;
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates a SCO channel connection as a virtual voice call to the current active device
-     * Active handsfree device will be notified of incoming call and connected call.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
-     *
-     * <p> {@link #EXTRA_STATE} will transition from
-     * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
-     * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
-     * in case of failure to establish the audio connection.
-     *
-     * @return true if successful, false if one of the following case applies
-     *  - SCO audio is not idle (connecting or connected)
-     *  - virtual call has already started
-     *  - there is no active device
-     *  - a Telecom managed call is going on
-     *  - binder is dead or Bluetooth is disabled or other error
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean startScoUsingVirtualVoiceCall() {
-        if (DBG) log("startScoUsingVirtualVoiceCall()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.startScoUsingVirtualVoiceCall(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Terminates an ongoing SCO connection and the associated virtual call.
-     *
-     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
-     * If this function returns true, this intent will be broadcasted with
-     * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
-     *
-     * @return true if successful, false if one of the following case applies
-     *  - virtual voice call is not started or has ended
-     *  - binder is dead or Bluetooth is disabled or other error
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean stopScoUsingVirtualVoiceCall() {
-        if (DBG) log("stopScoUsingVirtualVoiceCall()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Notify Headset of phone state change.
-     * This is a backdoor for phone app to call BluetoothHeadset since
-     * there is currently not a good way to get precise call state change outside
-     * of phone app.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
-            int type, String name) {
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                service.phoneStateChanged(numActive, numHeld, callState, number, type, name,
-                        mAttributionSource);
-            } catch (RemoteException  e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Send Headset of CLCC response
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
-            String number, int type) {
-        final IBluetoothHeadset service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.clccResponse(index, direction, status, mode, mpty, number, type,
-                        mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Sends a vendor-specific unsolicited result code to the headset.
-     *
-     * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code
-     * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the
-     * string <code>"+ANDROID: 0"</code> will be sent.
-     *
-     * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
-     *
-     * @param device Bluetooth headset.
-     * @param command A vendor-specific command.
-     * @param arg The argument that will be attached to the command.
-     * @return {@code false} if there is no headset connected, or if the command is not an allowed
-     * vendor-specific unsolicited result code, or on error. {@code true} otherwise.
-     * @throws IllegalArgumentException if {@code command} is {@code null}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
-            String arg) {
-        if (DBG) {
-            log("sendVendorSpecificResultCode()");
-        }
-        if (command == null) {
-            throw new IllegalArgumentException("command is null");
-        }
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendVendorSpecificResultCode(device, command, arg,
-                        mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, in HFP and HSP profiles,
-     * it is the device used for phone call audio. If a remote device is not
-     * connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     * @param device Remote Bluetooth Device, could be null if phone call audio should not be
-     * streamed to a headset
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) {
-            Log.d(TAG, "setActiveDevice: " + device);
-        }
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && (device == null || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected device that is active.
-     *
-     * @return the connected device that is active or null if no device
-     * is active.
-     * @hide
-     */
-    @UnsupportedAppUsage(trackingBug = 171933273)
-    @Nullable
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getActiveDevice() {
-        if (VDBG) Log.d(TAG, "getActiveDevice");
-        final IBluetoothHeadset service = mService;
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevice(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
-     * active connection.
-     *
-     * @return true if in-band ringing is enabled, false if in-band ringing is disabled
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean isInbandRingingEnabled() {
-        if (DBG) log("isInbandRingingEnabled()");
-        final IBluetoothHeadset service = mService;
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isInbandRingingEnabled(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check if in-band ringing is supported for this platform.
-     *
-     * @return true if in-band ringing is supported, false if in-band ringing is not supported
-     * @hide
-     */
-    public static boolean isInbandRingingSupported(Context context) {
-        return context.getResources().getBoolean(
-                com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothProfileServiceConnection mConnection =
-            new IBluetoothProfileServiceConnection.Stub() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "Proxy object connected");
-            mService = IBluetoothHeadset.Stub.asInterface(service);
-            mHandler.sendMessage(mHandler.obtainMessage(
-                    MESSAGE_HEADSET_SERVICE_CONNECTED));
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) Log.d(TAG, "Proxy object disconnected");
-            doUnbind();
-            mHandler.sendMessage(mHandler.obtainMessage(
-                    MESSAGE_HEADSET_SERVICE_DISCONNECTED));
-        }
-    };
-
-    @UnsupportedAppUsage
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private boolean isDisabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_HEADSET_SERVICE_CONNECTED: {
-                    if (mServiceListener != null) {
-                        mServiceListener.onServiceConnected(BluetoothProfile.HEADSET,
-                                BluetoothHeadset.this);
-                    }
-                    break;
-                }
-                case MESSAGE_HEADSET_SERVICE_DISCONNECTED: {
-                    if (mServiceListener != null) {
-                        mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
-                    }
-                    break;
-                }
-            }
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
deleted file mode 100644
index 7d7a7f7..0000000
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ /dev/null
@@ -1,1356 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Public API to control Hands Free Profile (HFP role only).
- * <p>
- * This class defines methods that shall be used by application to manage profile
- * connection, calls states and calls actions.
- * <p>
- *
- * @hide
- */
-public final class BluetoothHeadsetClient implements BluetoothProfile {
-    private static final String TAG = "BluetoothHeadsetClient";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent sent whenever connection to remote changes.
-     *
-     * <p>It includes two extras:
-     * <code>BluetoothProfile.EXTRA_PREVIOUS_STATE</code>
-     * and <code>BluetoothProfile.EXTRA_STATE</code>, which
-     * are mandatory.
-     * <p>There are also non mandatory feature extras:
-     * {@link #EXTRA_AG_FEATURE_3WAY_CALLING},
-     * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION},
-     * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT},
-     * {@link #EXTRA_AG_FEATURE_REJECT_CALL},
-     * {@link #EXTRA_AG_FEATURE_ECC},
-     * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD},
-     * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL},
-     * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL},
-     * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT},
-     * {@link #EXTRA_AG_FEATURE_MERGE},
-     * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH},
-     * sent as boolean values only when <code>EXTRA_STATE</code>
-     * is set to <code>STATE_CONNECTED</code>.</p>
-     *
-     * <p>Note that features supported by AG are being sent as
-     * booleans with value <code>true</code>,
-     * and not supported ones are <strong>not</strong> being sent at all.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent sent whenever audio state changes.
-     *
-     * <p>It includes two mandatory extras:
-     * {@link BluetoothProfile#EXTRA_STATE},
-     * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE},
-     * with possible values:
-     * {@link #STATE_AUDIO_CONNECTING},
-     * {@link #STATE_AUDIO_CONNECTED},
-     * {@link #STATE_AUDIO_DISCONNECTED}</p>
-     * <p>When <code>EXTRA_STATE</code> is set
-     * to </code>STATE_AUDIO_CONNECTED</code>,
-     * it also includes {@link #EXTRA_AUDIO_WBS}
-     * indicating wide band speech support.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED";
-
-    /**
-     * Intent sending updates of the Audio Gateway state.
-     * Each extra is being sent only when value it
-     * represents has been changed recently on AG.
-     * <p>It can contain one or more of the following extras:
-     * {@link #EXTRA_NETWORK_STATUS},
-     * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH},
-     * {@link #EXTRA_NETWORK_ROAMING},
-     * {@link #EXTRA_BATTERY_LEVEL},
-     * {@link #EXTRA_OPERATOR_NAME},
-     * {@link #EXTRA_VOICE_RECOGNITION},
-     * {@link #EXTRA_IN_BAND_RING}</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_AG_EVENT =
-            "android.bluetooth.headsetclient.profile.action.AG_EVENT";
-
-    /**
-     * Intent sent whenever state of a call changes.
-     *
-     * <p>It includes:
-     * {@link #EXTRA_CALL},
-     * with value of {@link BluetoothHeadsetClientCall} instance,
-     * representing actual call state.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CALL_CHANGED =
-            "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";
-
-    /**
-     * Intent that notifies about the result of the last issued action.
-     * Please note that not every action results in explicit action result code being sent.
-     * Instead other notifications about new Audio Gateway state might be sent,
-     * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value
-     * when for example user started voice recognition from HF unit.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_RESULT =
-            "android.bluetooth.headsetclient.profile.action.RESULT";
-
-    /**
-     * Intent that notifies about vendor specific event arrival. Events not defined in
-     * HFP spec will be matched with supported vendor event list and this intent will
-     * be broadcasted upon a match. Supported vendor events are of format of
-     * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx".
-     * Vendor event can be a response to an vendor specific command or unsolicited.
-     *
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT =
-            "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT";
-
-    /**
-     * Intent that notifies about the number attached to the last voice tag
-     * recorded on AG.
-     *
-     * <p>It contains:
-     * {@link #EXTRA_NUMBER},
-     * with a <code>String</code> value representing phone number.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LAST_VTAG =
-            "android.bluetooth.headsetclient.profile.action.LAST_VTAG";
-
-    public static final int STATE_AUDIO_DISCONNECTED = 0;
-    public static final int STATE_AUDIO_CONNECTING = 1;
-    public static final int STATE_AUDIO_CONNECTED = 2;
-
-    /**
-     * Extra with information if connected audio is WBS.
-     * <p>Possible values: <code>true</code>,
-     * <code>false</code>.</p>
-     */
-    public static final String EXTRA_AUDIO_WBS =
-            "android.bluetooth.headsetclient.extra.AUDIO_WBS";
-
-    /**
-     * Extra for AG_EVENT indicates network status.
-     * <p>Value: 0 - network unavailable,
-     * 1 - network available </p>
-     */
-    public static final String EXTRA_NETWORK_STATUS =
-            "android.bluetooth.headsetclient.extra.NETWORK_STATUS";
-    /**
-     * Extra for AG_EVENT intent indicates network signal strength.
-     * <p>Value: <code>Integer</code> representing signal strength.</p>
-     */
-    public static final String EXTRA_NETWORK_SIGNAL_STRENGTH =
-            "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH";
-    /**
-     * Extra for AG_EVENT intent indicates roaming state.
-     * <p>Value: 0 - no roaming
-     * 1 - active roaming</p>
-     */
-    public static final String EXTRA_NETWORK_ROAMING =
-            "android.bluetooth.headsetclient.extra.NETWORK_ROAMING";
-    /**
-     * Extra for AG_EVENT intent indicates the battery level.
-     * <p>Value: <code>Integer</code> representing signal strength.</p>
-     */
-    public static final String EXTRA_BATTERY_LEVEL =
-            "android.bluetooth.headsetclient.extra.BATTERY_LEVEL";
-    /**
-     * Extra for AG_EVENT intent indicates operator name.
-     * <p>Value: <code>String</code> representing operator name.</p>
-     */
-    public static final String EXTRA_OPERATOR_NAME =
-            "android.bluetooth.headsetclient.extra.OPERATOR_NAME";
-    /**
-     * Extra for AG_EVENT intent indicates voice recognition state.
-     * <p>Value:
-     * 0 - voice recognition stopped,
-     * 1 - voice recognition started.</p>
-     */
-    public static final String EXTRA_VOICE_RECOGNITION =
-            "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION";
-    /**
-     * Extra for AG_EVENT intent indicates in band ring state.
-     * <p>Value:
-     * 0 - in band ring tone not supported, or
-     * 1 - in band ring tone supported.</p>
-     */
-    public static final String EXTRA_IN_BAND_RING =
-            "android.bluetooth.headsetclient.extra.IN_BAND_RING";
-
-    /**
-     * Extra for AG_EVENT intent indicates subscriber info.
-     * <p>Value: <code>String</code> containing subscriber information.</p>
-     */
-    public static final String EXTRA_SUBSCRIBER_INFO =
-            "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO";
-
-    /**
-     * Extra for AG_CALL_CHANGED intent indicates the
-     * {@link BluetoothHeadsetClientCall} object that has changed.
-     */
-    public static final String EXTRA_CALL =
-            "android.bluetooth.headsetclient.extra.CALL";
-
-    /**
-     * Extra for ACTION_LAST_VTAG intent.
-     * <p>Value: <code>String</code> representing phone number
-     * corresponding to last voice tag recorded on AG</p>
-     */
-    public static final String EXTRA_NUMBER =
-            "android.bluetooth.headsetclient.extra.NUMBER";
-
-    /**
-     * Extra for ACTION_RESULT intent that shows the result code of
-     * last issued action.
-     * <p>Possible results:
-     * {@link #ACTION_RESULT_OK},
-     * {@link #ACTION_RESULT_ERROR},
-     * {@link #ACTION_RESULT_ERROR_NO_CARRIER},
-     * {@link #ACTION_RESULT_ERROR_BUSY},
-     * {@link #ACTION_RESULT_ERROR_NO_ANSWER},
-     * {@link #ACTION_RESULT_ERROR_DELAYED},
-     * {@link #ACTION_RESULT_ERROR_BLACKLISTED},
-     * {@link #ACTION_RESULT_ERROR_CME}</p>
-     */
-    public static final String EXTRA_RESULT_CODE =
-            "android.bluetooth.headsetclient.extra.RESULT_CODE";
-
-    /**
-     * Extra for ACTION_RESULT intent that shows the extended result code of
-     * last issued action.
-     * <p>Value: <code>Integer</code> - error code.</p>
-     */
-    public static final String EXTRA_CME_CODE =
-            "android.bluetooth.headsetclient.extra.CME_CODE";
-
-    /**
-     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
-     * indicates vendor ID.
-     */
-    public static final String EXTRA_VENDOR_ID =
-            "android.bluetooth.headsetclient.extra.VENDOR_ID";
-
-     /**
-     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
-     * indicates vendor event code.
-     */
-    public static final String EXTRA_VENDOR_EVENT_CODE =
-            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE";
-
-     /**
-     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
-     * contains full vendor event including event code and full arguments.
-     */
-    public static final String EXTRA_VENDOR_EVENT_FULL_ARGS =
-            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS";
-
-
-    /* Extras for AG_FEATURES, extras type is boolean */
-    // TODO verify if all of those are actually useful
-    /**
-     * AG feature: three way calling.
-     */
-    public static final String EXTRA_AG_FEATURE_3WAY_CALLING =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING";
-    /**
-     * AG feature: voice recognition.
-     */
-    public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION";
-    /**
-     * AG feature: fetching phone number for voice tagging procedure.
-     */
-    public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT";
-    /**
-     * AG feature: ability to reject incoming call.
-     */
-    public static final String EXTRA_AG_FEATURE_REJECT_CALL =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL";
-    /**
-     * AG feature: enhanced call handling (terminate specific call, private consultation).
-     */
-    public static final String EXTRA_AG_FEATURE_ECC =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC";
-    /**
-     * AG feature: response and hold.
-     */
-    public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD";
-    /**
-     * AG call handling feature: accept held or waiting call in three way calling scenarios.
-     */
-    public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL";
-    /**
-     * AG call handling feature: release held or waiting call in three way calling scenarios.
-     */
-    public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL";
-    /**
-     * AG call handling feature: release active call and accept held or waiting call in three way
-     * calling scenarios.
-     */
-    public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT";
-    /**
-     * AG call handling feature: merge two calls, held and active - multi party conference mode.
-     */
-    public static final String EXTRA_AG_FEATURE_MERGE =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE";
-    /**
-     * AG call handling feature: merge calls and disconnect from multi party
-     * conversation leaving peers connected to each other.
-     * Note that this feature needs to be supported by mobile network operator
-     * as it requires connection and billing transfer.
-     */
-    public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH =
-            "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH";
-
-    /* Action result codes */
-    public static final int ACTION_RESULT_OK = 0;
-    public static final int ACTION_RESULT_ERROR = 1;
-    public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2;
-    public static final int ACTION_RESULT_ERROR_BUSY = 3;
-    public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4;
-    public static final int ACTION_RESULT_ERROR_DELAYED = 5;
-    public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6;
-    public static final int ACTION_RESULT_ERROR_CME = 7;
-
-    /* Detailed CME error codes */
-    public static final int CME_PHONE_FAILURE = 0;
-    public static final int CME_NO_CONNECTION_TO_PHONE = 1;
-    public static final int CME_OPERATION_NOT_ALLOWED = 3;
-    public static final int CME_OPERATION_NOT_SUPPORTED = 4;
-    public static final int CME_PHSIM_PIN_REQUIRED = 5;
-    public static final int CME_PHFSIM_PIN_REQUIRED = 6;
-    public static final int CME_PHFSIM_PUK_REQUIRED = 7;
-    public static final int CME_SIM_NOT_INSERTED = 10;
-    public static final int CME_SIM_PIN_REQUIRED = 11;
-    public static final int CME_SIM_PUK_REQUIRED = 12;
-    public static final int CME_SIM_FAILURE = 13;
-    public static final int CME_SIM_BUSY = 14;
-    public static final int CME_SIM_WRONG = 15;
-    public static final int CME_INCORRECT_PASSWORD = 16;
-    public static final int CME_SIM_PIN2_REQUIRED = 17;
-    public static final int CME_SIM_PUK2_REQUIRED = 18;
-    public static final int CME_MEMORY_FULL = 20;
-    public static final int CME_INVALID_INDEX = 21;
-    public static final int CME_NOT_FOUND = 22;
-    public static final int CME_MEMORY_FAILURE = 23;
-    public static final int CME_TEXT_STRING_TOO_LONG = 24;
-    public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25;
-    public static final int CME_DIAL_STRING_TOO_LONG = 26;
-    public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27;
-    public static final int CME_NO_NETWORK_SERVICE = 30;
-    public static final int CME_NETWORK_TIMEOUT = 31;
-    public static final int CME_EMERGENCY_SERVICE_ONLY = 32;
-    public static final int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33;
-    public static final int CME_NOT_SUPPORTED_FOR_VOIP = 34;
-    public static final int CME_SIP_RESPONSE_CODE = 35;
-    public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40;
-    public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41;
-    public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42;
-    public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43;
-    public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44;
-    public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45;
-    public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46;
-    public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47;
-    public static final int CME_HIDDEN_KEY_REQUIRED = 48;
-    public static final int CME_EAP_NOT_SUPPORTED = 49;
-    public static final int CME_INCORRECT_PARAMETERS = 50;
-
-    /* Action policy for other calls when accepting call */
-    public static final int CALL_ACCEPT_NONE = 0;
-    public static final int CALL_ACCEPT_HOLD = 1;
-    public static final int CALL_ACCEPT_TERMINATE = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT,
-                    "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
-                @Override
-                public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
-                    return IBluetoothHeadsetClient.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothHeadsetClient proxy object.
-     */
-    /* package */ BluetoothHeadsetClient(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothHeadsetClient will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     */
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHeadsetClient getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Connects to remote device.
-     *
-     * Currently, the system supports only 1 connection. So, in case of the
-     * second connection, this implementation will disconnect already connected
-     * device automatically and will process the new one.
-     *
-     * @param device a remote device we want connect to
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Disconnects remote device
-     *
-     * @param device a remote device we want disconnect
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Return the list of connected remote devices
-     *
-     * @return list of connected devices; empty list if nothing is connected.
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHeadsetClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns list of remote devices in a particular state
-     *
-     * @param states collection of states
-     * @return list of devices that state matches the states listed in <code>states</code>; empty
-     * list if nothing matches the <code>states</code>
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHeadsetClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns state of the <code>device</code>
-     *
-     * @param device a remote device
-     * @return the state of connection of the device
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (VDBG) log("getConnectionState(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothHeadsetClient service = getService();
-        final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Starts voice recognition.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
-     * is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean startVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("startVoiceRecognition()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.startVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send vendor specific AT command.
-     *
-     * @param device remote device
-     * @param vendorId vendor number by Bluetooth SIG
-     * @param atCommand command to be sent. It start with + prefix and only one command at one time.
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
-        if (DBG) log("sendVendorSpecificCommand()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Stops voice recognition.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
-     * is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean stopVoiceRecognition(BluetoothDevice device) {
-        if (DBG) log("stopVoiceRecognition()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.stopVoiceRecognition(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns list of all calls in any state.
-     *
-     * @param device remote device
-     * @return list of calls; empty list if none call exists
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
-        if (DBG) log("getCurrentCalls()");
-        final IBluetoothHeadsetClient service = getService();
-        final List<BluetoothHeadsetClientCall> defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothHeadsetClientCall>> recv =
-                        new SynchronousResultReceiver();
-                service.getCurrentCalls(device, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns list of current values of AG indicators.
-     *
-     * @param device remote device
-     * @return bundle of AG  indicators; null if device is not in CONNECTED state
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public Bundle getCurrentAgEvents(BluetoothDevice device) {
-        if (DBG) log("getCurrentAgEvents()");
-        final IBluetoothHeadsetClient service = getService();
-        final Bundle defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
-                service.getCurrentAgEvents(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Accepts a call
-     *
-     * @param device remote device
-     * @param flag action policy while accepting a call. Possible values {@link #CALL_ACCEPT_NONE},
-     * {@link #CALL_ACCEPT_HOLD}, {@link #CALL_ACCEPT_TERMINATE}
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean acceptCall(BluetoothDevice device, int flag) {
-        if (DBG) log("acceptCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.acceptCall(device, flag, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Holds a call.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean holdCall(BluetoothDevice device) {
-        if (DBG) log("holdCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.holdCall(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Rejects a call.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not
-     * supported.</p>
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean rejectCall(BluetoothDevice device) {
-        if (DBG) log("rejectCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.rejectCall(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Terminates a specified call.
-     *
-     * Works only when Extended Call Control is supported by Audio Gateway.
-     *
-     * @param device remote device
-     * @param call Handle of call obtained in {@link #dial(BluetoothDevice, String)} or obtained via
-     * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active
-     * calls.
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
-     * supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
-        if (DBG) log("terminateCall()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.terminateCall(device, call, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Enters private mode with a specified call.
-     *
-     * Works only when Extended Call Control is supported by Audio Gateway.
-     *
-     * @param device remote device
-     * @param index index of the call to connect in private mode
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
-     * supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean enterPrivateMode(BluetoothDevice device, int index) {
-        if (DBG) log("enterPrivateMode()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.enterPrivateMode(device, index, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Performs explicit call transfer.
-     *
-     * That means connect other calls and disconnect.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature
-     * is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean explicitCallTransfer(BluetoothDevice device) {
-        if (DBG) log("explicitCallTransfer()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.explicitCallTransfer(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Places a call with specified number.
-     *
-     * @param device remote device
-     * @param number valid phone number
-     * @return <code>{@link BluetoothHeadsetClientCall} call</code> if command has been issued
-     * successfully; <code>{@link null}</code> otherwise; upon completion HFP sends {@link
-     * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
-        if (DBG) log("dial()");
-        final IBluetoothHeadsetClient service = getService();
-        final BluetoothHeadsetClientCall defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<BluetoothHeadsetClientCall> recv =
-                        new SynchronousResultReceiver();
-                service.dial(device, number, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends DTMF code.
-     *
-     * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,#
-     *
-     * @param device remote device
-     * @param code ASCII code
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendDTMF(BluetoothDevice device, byte code) {
-        if (DBG) log("sendDTMF()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendDTMF(device, code, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get a number corresponding to last voice tag recorded on AG.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_LAST_VTAG} or {@link #ACTION_RESULT}
-     * intent;
-     *
-     * <p>Feature required for successful execution is being reported by: {@link
-     * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when
-     * feature is not supported.</p>
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getLastVoiceTagNumber(BluetoothDevice device) {
-        if (DBG) log("getLastVoiceTagNumber()");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getLastVoiceTagNumber(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns current audio state of Audio Gateway.
-     *
-     * Note: This is an internal function and shouldn't be exposed
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getAudioState(BluetoothDevice device) {
-        if (VDBG) log("getAudioState");
-        final IBluetoothHeadsetClient service = getService();
-        final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getAudioState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        } else {
-            return defaultValue;
-        }
-        return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
-    }
-
-    /**
-     * Sets whether audio routing is allowed.
-     *
-     * @param device remote device
-     * @param allowed if routing is allowed to the device Note: This is an internal function and
-     * shouldn't be exposed
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
-        if (VDBG) log("setAudioRouteAllowed");
-        final IBluetoothHeadsetClient service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Returns whether audio routing is allowed.
-     *
-     * @param device remote device
-     * @return whether the command succeeded Note: This is an internal function and shouldn't be
-     * exposed
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getAudioRouteAllowed(BluetoothDevice device) {
-        if (VDBG) log("getAudioRouteAllowed");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getAudioRouteAllowed(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates a connection of audio channel.
-     *
-     * It setup SCO channel with remote connected Handsfree AG device.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connectAudio(BluetoothDevice device) {
-        if (VDBG) log("connectAudio");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connectAudio(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Disconnects audio channel.
-     *
-     * It tears down the SCO channel from remote AG device.
-     *
-     * @param device remote device
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnectAudio(BluetoothDevice device) {
-        if (VDBG) log("disconnectAudio");
-        final IBluetoothHeadsetClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnectAudio(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get Audio Gateway features
-     *
-     * @param device remote device
-     * @return bundle of AG features; null if no service or AG not connected
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public Bundle getCurrentAgFeatures(BluetoothDevice device) {
-        if (VDBG) log("getCurrentAgFeatures");
-        final IBluetoothHeadsetClient service = getService();
-        final Bundle defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
-                service.getCurrentAgFeatures(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
deleted file mode 100644
index 032b507..0000000
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-
-import java.util.UUID;
-
-/**
- * This class represents a single call, its state and properties.
- * It implements {@link Parcelable} for inter-process message passing.
- *
- * @hide
- */
-public final class BluetoothHeadsetClientCall implements Parcelable, Attributable {
-
-    /* Call state */
-    /**
-     * Call is active.
-     */
-    public static final int CALL_STATE_ACTIVE = 0;
-    /**
-     * Call is in held state.
-     */
-    public static final int CALL_STATE_HELD = 1;
-    /**
-     * Outgoing call that is being dialed right now.
-     */
-    public static final int CALL_STATE_DIALING = 2;
-    /**
-     * Outgoing call that remote party has already been alerted about.
-     */
-    public static final int CALL_STATE_ALERTING = 3;
-    /**
-     * Incoming call that can be accepted or rejected.
-     */
-    public static final int CALL_STATE_INCOMING = 4;
-    /**
-     * Waiting call state when there is already an active call.
-     */
-    public static final int CALL_STATE_WAITING = 5;
-    /**
-     * Call that has been held by response and hold
-     * (see Bluetooth specification for further references).
-     */
-    public static final int CALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6;
-    /**
-     * Call that has been already terminated and should not be referenced as a valid call.
-     */
-    public static final int CALL_STATE_TERMINATED = 7;
-
-    private final BluetoothDevice mDevice;
-    private final int mId;
-    private int mState;
-    private String mNumber;
-    private boolean mMultiParty;
-    private final boolean mOutgoing;
-    private final UUID mUUID;
-    private final long mCreationElapsedMilli;
-    private final boolean mInBandRing;
-
-    /**
-     * Creates BluetoothHeadsetClientCall instance.
-     */
-    public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number,
-            boolean multiParty, boolean outgoing, boolean inBandRing) {
-        this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing);
-    }
-
-    public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state,
-            String number, boolean multiParty, boolean outgoing, boolean inBandRing) {
-        mDevice = device;
-        mId = id;
-        mUUID = uuid;
-        mState = state;
-        mNumber = number != null ? number : "";
-        mMultiParty = multiParty;
-        mOutgoing = outgoing;
-        mInBandRing = inBandRing;
-        mCreationElapsedMilli = SystemClock.elapsedRealtime();
-    }
-
-    /** {@hide} */
-    public void setAttributionSource(@NonNull AttributionSource attributionSource) {
-        Attributable.setAttributionSource(mDevice, attributionSource);
-    }
-
-    /**
-     * Sets call's state.
-     *
-     * <p>Note: This is an internal function and shouldn't be exposed</p>
-     *
-     * @param state new call state.
-     */
-    public void setState(int state) {
-        mState = state;
-    }
-
-    /**
-     * Sets call's number.
-     *
-     * <p>Note: This is an internal function and shouldn't be exposed</p>
-     *
-     * @param number String representing phone number.
-     */
-    public void setNumber(String number) {
-        mNumber = number;
-    }
-
-    /**
-     * Sets this call as multi party call.
-     *
-     * <p>Note: This is an internal function and shouldn't be exposed</p>
-     *
-     * @param multiParty if <code>true</code> sets this call as a part of multi party conference.
-     */
-    public void setMultiParty(boolean multiParty) {
-        mMultiParty = multiParty;
-    }
-
-    /**
-     * Gets call's device.
-     *
-     * @return call device.
-     */
-    public BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Gets call's Id.
-     *
-     * @return call id.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int getId() {
-        return mId;
-    }
-
-    /**
-     * Gets call's UUID.
-     *
-     * @return call uuid
-     * @hide
-     */
-    public UUID getUUID() {
-        return mUUID;
-    }
-
-    /**
-     * Gets call's current state.
-     *
-     * @return state of this particular phone call.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int getState() {
-        return mState;
-    }
-
-    /**
-     * Gets call's number.
-     *
-     * @return string representing phone number.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public String getNumber() {
-        return mNumber;
-    }
-
-    /**
-     * Gets call's creation time in millis since epoch.
-     *
-     * @return long representing the creation time.
-     */
-    public long getCreationElapsedMilli() {
-        return mCreationElapsedMilli;
-    }
-
-    /**
-     * Checks if call is an active call in a conference mode (aka multi party).
-     *
-     * @return <code>true</code> if call is a multi party call, <code>false</code> otherwise.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean isMultiParty() {
-        return mMultiParty;
-    }
-
-    /**
-     * Checks if this call is an outgoing call.
-     *
-     * @return <code>true</code> if its outgoing call, <code>false</code> otherwise.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean isOutgoing() {
-        return mOutgoing;
-    }
-
-    /**
-     * Checks if the ringtone will be generated by the connected phone
-     *
-     * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise.
-     */
-    public boolean isInBandRing() {
-        return mInBandRing;
-    }
-
-
-    @Override
-    public String toString() {
-        return toString(false);
-    }
-
-    /**
-     * Generate a log string for this call
-     * @param loggable whether device address should be logged
-     * @return log string
-     */
-    public String toString(boolean loggable) {
-        StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: ");
-        builder.append(loggable ? mDevice : mDevice.hashCode());
-        builder.append(", mId: ");
-        builder.append(mId);
-        builder.append(", mUUID: ");
-        builder.append(mUUID);
-        builder.append(", mState: ");
-        switch (mState) {
-            case CALL_STATE_ACTIVE:
-                builder.append("ACTIVE");
-                break;
-            case CALL_STATE_HELD:
-                builder.append("HELD");
-                break;
-            case CALL_STATE_DIALING:
-                builder.append("DIALING");
-                break;
-            case CALL_STATE_ALERTING:
-                builder.append("ALERTING");
-                break;
-            case CALL_STATE_INCOMING:
-                builder.append("INCOMING");
-                break;
-            case CALL_STATE_WAITING:
-                builder.append("WAITING");
-                break;
-            case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
-                builder.append("HELD_BY_RESPONSE_AND_HOLD");
-                break;
-            case CALL_STATE_TERMINATED:
-                builder.append("TERMINATED");
-                break;
-            default:
-                builder.append(mState);
-                break;
-        }
-        builder.append(", mNumber: ");
-        builder.append(loggable ? mNumber : mNumber.hashCode());
-        builder.append(", mMultiParty: ");
-        builder.append(mMultiParty);
-        builder.append(", mOutgoing: ");
-        builder.append(mOutgoing);
-        builder.append(", mInBandRing: ");
-        builder.append(mInBandRing);
-        builder.append("}");
-        return builder.toString();
-    }
-
-    /**
-     * {@link Parcelable.Creator} interface implementation.
-     */
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHeadsetClientCall> CREATOR =
-            new Parcelable.Creator<BluetoothHeadsetClientCall>() {
-                @Override
-                public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
-                    return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null),
-                            in.readInt(), UUID.fromString(in.readString()), in.readInt(),
-                            in.readString(), in.readInt() == 1, in.readInt() == 1,
-                            in.readInt() == 1);
-                }
-
-                @Override
-                public BluetoothHeadsetClientCall[] newArray(int size) {
-                    return new BluetoothHeadsetClientCall[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mDevice, 0);
-        out.writeInt(mId);
-        out.writeString(mUUID.toString());
-        out.writeInt(mState);
-        out.writeString(mNumber);
-        out.writeInt(mMultiParty ? 1 : 0);
-        out.writeInt(mOutgoing ? 1 : 0);
-        out.writeInt(mInBandRing ? 1 : 0);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
deleted file mode 100644
index 65f68a9..0000000
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2011 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.bluetooth;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Public API for Bluetooth Health Profile.
- *
- * <p>BluetoothHealth is a proxy object for controlling the Bluetooth
- * Service via IPC.
- *
- * <p> How to connect to a health device which is acting in the source role.
- * <li> Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHealth proxy object. </li>
- * <li> Create an {@link BluetoothHealth} callback and call
- * {@link #registerSinkAppConfiguration} to register an application
- * configuration </li>
- * <li> Pair with the remote device. This currently needs to be done manually
- * from Bluetooth Settings </li>
- * <li> Connect to a health device using {@link #connectChannelToSource}. Some
- * devices will connect the channel automatically. The {@link BluetoothHealth}
- * callback will inform the application of channel state change. </li>
- * <li> Use the file descriptor provided with a connected channel to read and
- * write data to the health channel. </li>
- * <li> The received data needs to be interpreted using a health manager which
- * implements the IEEE 11073-xxxxx specifications.
- * <li> When done, close the health channel by calling {@link #disconnectChannel}
- * and unregister the application configuration calling
- * {@link #unregisterAppConfiguration}
- *
- * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps
- * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
- * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
- * {@link BluetoothDevice#createL2capChannel(int)}
- */
-@Deprecated
-public final class BluetoothHealth implements BluetoothProfile {
-    private static final String TAG = "BluetoothHealth";
-    /**
-     * Health Profile Source Role - the health device.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int SOURCE_ROLE = 1 << 0;
-
-    /**
-     * Health Profile Sink Role the device talking to the health device.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int SINK_ROLE = 1 << 1;
-
-    /**
-     * Health Profile - Channel Type used - Reliable
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int CHANNEL_TYPE_RELIABLE = 10;
-
-    /**
-     * Health Profile - Channel Type used - Streaming
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int CHANNEL_TYPE_STREAMING = 11;
-
-    /**
-     * Hide auto-created default constructor
-     * @hide
-     */
-    BluetoothHealth() {}
-
-    /**
-     * Register an application configuration that acts as a Health SINK.
-     * This is the configuration that will be used to communicate with health devices
-     * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
-     * the callback is used to notify success or failure if the function returns true.
-     *
-     * @param name The friendly name associated with the application or configuration.
-     * @param dataType The dataType of the Source role of Health Profile to which the sink wants to
-     * connect to.
-     * @param callback A callback to indicate success or failure of the registration and all
-     * operations done on this application configuration.
-     * @return If true, callback will be called.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean registerSinkAppConfiguration(String name, int dataType,
-            BluetoothHealthCallback callback) {
-        Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Unregister an application configuration that has been registered using
-     * {@link #registerSinkAppConfiguration}
-     *
-     * @param config The health app configuration
-     * @return Success or failure.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
-        Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Connect to a health device which has the {@link #SOURCE_ROLE}.
-     * This is an asynchronous call. If this function returns true, the callback
-     * associated with the application configuration will be called.
-     *
-     * @param device The remote Bluetooth device.
-     * @param config The application configuration which has been registered using {@link
-     * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
-     * @return If true, the callback associated with the application config will be called.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean connectChannelToSource(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config) {
-        Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Disconnect a connected health channel.
-     * This is an asynchronous call. If this function returns true, the callback
-     * associated with the application configuration will be called.
-     *
-     * @param device The remote Bluetooth device.
-     * @param config The application configuration which has been registered using {@link
-     * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
-     * @param channelId The channel id associated with the channel
-     * @return If true, the callback associated with the application config will be called.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public boolean disconnectChannel(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config, int channelId) {
-        Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
-        return false;
-    }
-
-    /**
-     * Get the file descriptor of the main channel associated with the remote device
-     * and application configuration.
-     *
-     * <p> Its the responsibility of the caller to close the ParcelFileDescriptor
-     * when done.
-     *
-     * @param device The remote Bluetooth health device
-     * @param config The application configuration
-     * @return null on failure, ParcelFileDescriptor on success.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config) {
-        Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
-        return null;
-    }
-
-    /**
-     * Get the current connection state of the profile.
-     *
-     * This is not specific to any application configuration but represents the connection
-     * state of the local Bluetooth adapter with the remote device. This can be used
-     * by applications like status bar which would just like to know the state of the
-     * local adapter.
-     *
-     * @param device Remote bluetooth device.
-     * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public int getConnectionState(BluetoothDevice device) {
-        Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
-        return STATE_DISCONNECTED;
-    }
-
-    /**
-     * Get connected devices for the health profile.
-     *
-     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
-     *
-     * This is not specific to any application configuration but represents the connection
-     * state of the local Bluetooth adapter for this profile. This can be used
-     * by applications like status bar which would just like to know the state of the
-     * local adapter.
-     *
-     * @return List of devices. The list will be empty on error.
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public List<BluetoothDevice> getConnectedDevices() {
-        Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
-        return new ArrayList<>();
-    }
-
-    /**
-     * Get a list of devices that match any of the given connection
-     * states.
-     *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * <p>This is not specific to any application configuration but represents the connection
-     * state of the local Bluetooth adapter for this profile. This can be used
-     * by applications like status bar which would just like to know the state of the
-     * local adapter.
-     *
-     * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
-        return new ArrayList<>();
-    }
-
-    /** Health Channel Connection State - Disconnected
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_DISCONNECTED = 0;
-    /** Health Channel Connection State - Connecting
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_CONNECTING = 1;
-    /** Health Channel Connection State - Connected
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_CONNECTED = 2;
-    /** Health Channel Connection State - Disconnecting
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int STATE_CHANNEL_DISCONNECTING = 3;
-
-    /** Health App Configuration registration success
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
-    /** Health App Configuration registration failure
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
-    /** Health App Configuration un-registration success
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
-    /** Health App Configuration un-registration failure
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
-}
diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
deleted file mode 100644
index 2f66df2..0000000
--- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The Bluetooth Health Application Configuration that is used in conjunction with
- * the {@link BluetoothHealth} class. This class represents an application configuration
- * that the Bluetooth Health third party application will register to communicate with the
- * remote Bluetooth health device.
- *
- * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
- * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
- * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
- * {@link BluetoothDevice#createL2capChannel(int)}
- */
-@Deprecated
-public final class BluetoothHealthAppConfiguration implements Parcelable {
-
-    /**
-     * Hide auto-created default constructor
-     * @hide
-     */
-    BluetoothHealthAppConfiguration() {}
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Return the data type associated with this application configuration.
-     *
-     * @return dataType
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public int getDataType() {
-        return 0;
-    }
-
-    /**
-     * Return the name of the application configuration.
-     *
-     * @return String name
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public String getName() {
-        return null;
-    }
-
-    /**
-     * Return the role associated with this application configuration.
-     *
-     * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public int getRole() {
-        return 0;
-    }
-
-    /**
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR =
-            new Parcelable.Creator<BluetoothHealthAppConfiguration>() {
-                @Override
-                public BluetoothHealthAppConfiguration createFromParcel(Parcel in) {
-                    return new BluetoothHealthAppConfiguration();
-                }
-
-                @Override
-                public BluetoothHealthAppConfiguration[] newArray(int size) {
-                    return new BluetoothHealthAppConfiguration[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {}
-}
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
deleted file mode 100644
index 4769212..0000000
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2011 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.bluetooth;
-
-import android.annotation.BinderThread;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-/**
- * This abstract class is used to implement {@link BluetoothHealth} callbacks.
- *
- * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
- * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
- * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
- * {@link BluetoothDevice#createL2capChannel(int)}
- */
-@Deprecated
-public abstract class BluetoothHealthCallback {
-    private static final String TAG = "BluetoothHealthCallback";
-
-    /**
-     * Callback to inform change in registration state of the health
-     * application.
-     * <p> This callback is called on the binder thread (not on the UI thread)
-     *
-     * @param config Bluetooth Health app configuration
-     * @param status Success or failure of the registration or unregistration calls. Can be one of
-     * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or {@link
-     * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or
-     * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS}
-     * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @BinderThread
-    @Deprecated
-    public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
-            int status) {
-        Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
-    }
-
-    /**
-     * Callback to inform change in channel state.
-     * <p> Its the responsibility of the implementor of this callback to close the
-     * parcel file descriptor when done. This callback is called on the Binder
-     * thread (not the UI thread)
-     *
-     * @param config The Health app configutation
-     * @param device The Bluetooth Device
-     * @param prevState The previous state of the channel
-     * @param newState The new state of the channel.
-     * @param fd The Parcel File Descriptor when the channel state is connected.
-     * @param channelId The id associated with the channel. This id will be used in future calls
-     * like when disconnecting the channel.
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @BinderThread
-    @Deprecated
-    public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
-            BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
-            int channelId) {
-        Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device
-                + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd
-                + "ChannelId:" + channelId);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
deleted file mode 100644
index 339a75f..0000000
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Hearing Aid profile.
- *
- * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHearingAid proxy object.
- *
- * <p> Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each
- * method is protected with its appropriate permission.
- */
-public final class BluetoothHearingAid implements BluetoothProfile {
-    private static final String TAG = "BluetoothHearingAid";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Hearing Aid
-     * profile. Please note that in the binaural case, there will be two different LE devices for
-     * the left and right side and each device will have their own connection state changes.S
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * This device represents Left Hearing Aid.
-     *
-     * @hide
-     */
-    public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
-
-    /**
-     * This device represents Right Hearing Aid.
-     *
-     * @hide
-     */
-    public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
-
-    /**
-     * This device is Monaural.
-     *
-     * @hide
-     */
-    public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
-
-    /**
-     * This device is Binaural (should receive only left or right audio).
-     *
-     * @hide
-     */
-    public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
-
-    /**
-     * Indicates the HiSyncID could not be read and is unavailable.
-     *
-     * @hide
-     */
-    public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID,
-                    "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
-                @Override
-                public IBluetoothHearingAid getServiceInterface(IBinder service) {
-                    return IBluetoothHearingAid.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothHearingAid proxy object for interacting with the local
-     * Bluetooth Hearing Aid service.
-     */
-    /* package */ BluetoothHearingAid(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHearingAid getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHearingAid service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
-    @NonNull int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHearingAid service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @BluetoothProfile.BtProfileState int getConnectionState(
-    @NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, Hearing Aid audio
-     * streaming is to the active Hearing Aid device. If a remote device
-     * is not connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     * @param device the remote Bluetooth device. Could be null to clear
-     * the active device and stop streaming audio to a Bluetooth device.
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) log("setActiveDevice(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected physical Hearing Aid devices that are active
-     *
-     * @return the list of active devices. The first element is the left active
-     * device; the second element is the right active device. If either or both side
-     * is not active, it will be null on that position. Returns empty list on error.
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getActiveDevices() {
-        if (VDBG) log("getActiveDevices()");
-        final IBluetoothHearingAid service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        verifyDeviceNotNull(device, "setConnectionPolicy");
-        final IBluetoothHearingAid service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        verifyDeviceNotNull(device, "getConnectionPolicy");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    /**
-     * Tells remote device to set an absolute volume.
-     *
-     * @param volume Absolute volume to be set on remote
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void setVolume(int volume) {
-        if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
-        final IBluetoothHearingAid service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setVolume(volume, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Get the HiSyncId (unique hearing aid device identifier) of the device.
-     *
-     * <a href=https://source.android.com/devices/bluetooth/asha#hisyncid>HiSyncId documentation
-     * can be found here</a>
-     *
-     * @param device Bluetooth device
-     * @return the HiSyncId of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public long getHiSyncId(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getHiSyncId(" + device + ")");
-        verifyDeviceNotNull(device, "getConnectionPolicy");
-        final IBluetoothHearingAid service = getService();
-        final long defaultValue = HI_SYNC_ID_INVALID;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver();
-                service.getHiSyncId(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the side of the device.
-     *
-     * @param device Bluetooth device.
-     * @return SIDE_LEFT or SIDE_RIGHT
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getDeviceSide(BluetoothDevice device) {
-        if (VDBG) log("getDeviceSide(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = SIDE_LEFT;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getDeviceSide(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the mode of the device.
-     *
-     * @param device Bluetooth device
-     * @return MODE_MONAURAL or MODE_BINAURAL
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getDeviceMode(BluetoothDevice device) {
-        if (VDBG) log("getDeviceMode(" + device + ")");
-        final IBluetoothHearingAid service = getService();
-        final int defaultValue = MODE_MONAURAL;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getDeviceMode(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
-        if (device == null) {
-            Log.e(TAG, methodName + ": device param is null");
-            throw new IllegalArgumentException("Device cannot be null");
-        }
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
deleted file mode 100644
index 44a355b..0000000
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Provides the public APIs to control the Bluetooth HID Device profile.
- *
- * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC.
- * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
- */
-public final class BluetoothHidDevice implements BluetoothProfile {
-    private static final String TAG = BluetoothHidDevice.class.getSimpleName();
-    private static final boolean DBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Input Host profile.
-     *
-     * <p>This intent will have 3 extras:
-     *
-     * <ul>
-     *   <li>{@link #EXTRA_STATE} - The current state of the profile.
-     *   <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
-     *   <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link
-     * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link
-     * #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Constant representing unspecified HID device subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_NONE = (byte) 0x00;
-    /**
-     * Constant representing keyboard subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
-    /**
-     * Constant representing mouse subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_MOUSE = (byte) 0x80;
-    /**
-     * Constant representing combo keyboard and mouse subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS1_COMBO = (byte) 0xC0;
-
-    /**
-     * Constant representing uncategorized HID device subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00;
-    /**
-     * Constant representing joystick subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01;
-    /**
-     * Constant representing gamepad subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
-    /**
-     * Constant representing remote control subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
-    /**
-     * Constant representing sensing device subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
-    /**
-     * Constant representing digitizer tablet subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05;
-    /**
-     * Constant representing card reader subclass.
-     *
-     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
-     */
-    public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
-
-    /**
-     * Constant representing HID Input Report type.
-     *
-     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
-     */
-    public static final byte REPORT_TYPE_INPUT = (byte) 1;
-    /**
-     * Constant representing HID Output Report type.
-     *
-     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
-     */
-    public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
-    /**
-     * Constant representing HID Feature Report type.
-     *
-     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
-     */
-    public static final byte REPORT_TYPE_FEATURE = (byte) 3;
-
-    /**
-     * Constant representing success response for Set Report.
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_SUCCESS = (byte) 0;
-    /**
-     * Constant representing error response for Set Report due to "not ready".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_NOT_READY = (byte) 1;
-    /**
-     * Constant representing error response for Set Report due to "invalid report ID".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2;
-    /**
-     * Constant representing error response for Set Report due to "unsupported request".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3;
-    /**
-     * Constant representing error response for Set Report due to "invalid parameter".
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4;
-    /**
-     * Constant representing error response for Set Report with unknown reason.
-     *
-     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     */
-    public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
-
-    /**
-     * Constant representing boot protocol mode used set by host. Default is always {@link
-     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
-     *
-     * @see Callback#onSetProtocol(BluetoothDevice, byte)
-     */
-    public static final byte PROTOCOL_BOOT_MODE = (byte) 0;
-    /**
-     * Constant representing report protocol mode used set by host. Default is always {@link
-     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
-     *
-     * @see Callback#onSetProtocol(BluetoothDevice, byte)
-     */
-    public static final byte PROTOCOL_REPORT_MODE = (byte) 1;
-
-    /**
-     * The template class that applications use to call callback functions on events from the HID
-     * host. Callback functions are wrapped in this class and registered to the Android system
-     * during app registration.
-     */
-    public abstract static class Callback {
-
-        private static final String TAG = "BluetoothHidDevCallback";
-
-        /**
-         * Callback called when application registration state changes. Usually it's called due to
-         * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
-         * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
-         * unsolicited in case e.g. Bluetooth was turned off in which case application is
-         * unregistered automatically.
-         *
-         * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently
-         *     has Virtual Cable established with device. Only valid when application is registered,
-         *     can be <code>null</code>.
-         * @param registered <code>true</code> if application is registered, <code>false</code>
-         *     otherwise.
-         */
-        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            Log.d(
-                    TAG,
-                    "onAppStatusChanged: pluggedDevice="
-                            + pluggedDevice
-                            + " registered="
-                            + registered);
-        }
-
-        /**
-         * Callback called when connection state with remote host was changed. Application can
-         * assume than Virtual Cable is established when called with {@link
-         * BluetoothProfile#STATE_CONNECTED} <code>state</code>.
-         *
-         * @param device {@link BluetoothDevice} object representing host device which connection
-         *     state was changed.
-         * @param state Connection state as defined in {@link BluetoothProfile}.
-         */
-        public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
-        }
-
-        /**
-         * Callback called when GET_REPORT is received from remote host. Should be replied by
-         * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
-         * byte[])}.
-         *
-         * @param type Requested Report Type.
-         * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
-         * @param bufferSize Requested buffer size, application shall respond with at least given
-         *     number of bytes.
-         */
-        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            Log.d(
-                    TAG,
-                    "onGetReport: device="
-                            + device
-                            + " type="
-                            + type
-                            + " id="
-                            + id
-                            + " bufferSize="
-                            + bufferSize);
-        }
-
-        /**
-         * Callback called when SET_REPORT is received from remote host. In case received data are
-         * invalid, application shall respond with {@link
-         * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
-         *
-         * @param type Report Type.
-         * @param id Report Id.
-         * @param data Report data.
-         */
-        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
-        }
-
-        /**
-         * Callback called when SET_PROTOCOL is received from remote host. Application shall use
-         * this information to send only reports valid for given protocol mode. By default, {@link
-         * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
-         *
-         * @param protocol Protocol Mode.
-         */
-        public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
-        }
-
-        /**
-         * Callback called when report data is received over interrupt channel. Report Type is
-         * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
-         *
-         * @param reportId Report Id.
-         * @param data Report data.
-         */
-        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
-        }
-
-        /**
-         * Callback called when Virtual Cable is removed. After this callback is received connection
-         * will be disconnected automatically.
-         */
-        public void onVirtualCableUnplug(BluetoothDevice device) {
-            Log.d(TAG, "onVirtualCableUnplug: device=" + device);
-        }
-    }
-
-    private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
-
-        private final Executor mExecutor;
-        private final Callback mCallback;
-        private final AttributionSource mAttributionSource;
-
-        CallbackWrapper(Executor executor, Callback callback, AttributionSource attributionSource) {
-            mExecutor = executor;
-            mCallback = callback;
-            mAttributionSource = attributionSource;
-        }
-
-        @Override
-        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            Attributable.setAttributionSource(pluggedDevice, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void onVirtualCableUnplug(BluetoothDevice device) {
-            Attributable.setAttributionSource(device, mAttributionSource);
-            final long token = clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
-            } finally {
-                restoreCallingIdentity(token);
-            }
-        }
-    }
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE,
-                    "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
-                @Override
-                public IBluetoothHidDevice getServiceInterface(IBinder service) {
-                    return IBluetoothHidDevice.Stub.asInterface(service);
-                }
-    };
-
-    BluetoothHidDevice(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHidDevice getService() {
-        return mProfileConnector.getService();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        final IBluetoothHidDevice service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        final IBluetoothHidDevice service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        final IBluetoothHidDevice service = getService();
-        final int defaultValue = STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Registers application to be used for HID device. Connections to HID Device are only possible
-     * when application is registered. Only one application can be registered at one time. When an
-     * application is registered, the HID Host service will be disabled until it is unregistered.
-     * When no longer used, application should be unregistered using {@link #unregisterApp()}. The
-     * app will be automatically unregistered if it is not foreground. The registration status
-     * should be tracked by the application by handling callback from Callback#onAppStatusChanged.
-     * The app registration status is not related to the return value of this method.
-     *
-     * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID
-     *     Device SDP record is required.
-     * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The
-     *     Incoming QoS Settings is not required. Use null or default
-     *     BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The
-     *     Outgoing QoS Settings is not required. Use null or default
-     *     BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param executor {@link Executor} object on which callback will be executed. The Executor
-     *     object is required.
-     * @param callback {@link Callback} object to which callback messages will be sent. The Callback
-     *     object is required.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean registerApp(
-            BluetoothHidDeviceAppSdpSettings sdp,
-            BluetoothHidDeviceAppQosSettings inQos,
-            BluetoothHidDeviceAppQosSettings outQos,
-            Executor executor,
-            Callback callback) {
-        boolean result = false;
-
-        if (sdp == null) {
-            throw new IllegalArgumentException("sdp parameter cannot be null");
-        }
-
-        if (executor == null) {
-            throw new IllegalArgumentException("executor parameter cannot be null");
-        }
-
-        if (callback == null) {
-            throw new IllegalArgumentException("callback parameter cannot be null");
-        }
-
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = result;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
-                service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv);
-                result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Unregisters application. Active connection will be disconnected and no new connections will
-     * be allowed until registered again using {@link #registerApp
-     * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be
-     * tracked by the application by handling callback from Callback#onAppStatusChanged. The app
-     * registration status is not related to the return value of this method.
-     *
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean unregisterApp() {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.unregisterApp(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends report to remote host using interrupt channel.
-     *
-     * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in
-     *     descriptor.
-     * @param data Report data, not including Report Id.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendReport(device, id, data, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends report to remote host as reply for GET_REPORT request from {@link
-     * Callback#onGetReport(BluetoothDevice, byte, byte, int)}.
-     *
-     * @param type Report Type, as in request.
-     * @param id Report Id, as in request.
-     * @param data Report data, not including Report Id.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.replyReport(device, type, id, data, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Sends error handshake message as reply for invalid SET_REPORT request from {@link
-     * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
-     *
-     * @param error Error to be sent for SET_REPORT via HANDSHAKE.
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean reportError(BluetoothDevice device, byte error) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.reportError(device, error, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Gets the application name of the current HidDeviceService user.
-     *
-     * @return the current user name, or empty string if cannot get the name
-     * {@hide}
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public String getUserAppName() {
-        final IBluetoothHidDevice service = getService();
-        final String defaultValue = "";
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver();
-                service.getUserAppName(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiates connection to host which is currently paired with this device. If the application
-     * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
-     * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
-     * connection state is not related to the return value of this method.
-     *
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(BluetoothDevice device) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Disconnects from currently connected host. The connection state should be tracked by the
-     * application by handling callback from Callback#onConnectionStateChanged. The connection state
-     * is not related to the return value of this method.
-     *
-     * @return true if the command is successfully sent; otherwise false.
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}
-     * and disconnects Hid device if connectionPolicy is
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}.
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of:
-     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
-     * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy determines whether hid device should be connected or disconnected
-     * @return true if hid device is connected or disconnected, false otherwise
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothHidDevice service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        if (DBG) {
-            Log.d(TAG, msg);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
deleted file mode 100644
index b21ebe5..0000000
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device application.
- *
- * <p>The BluetoothHidDevice framework will update the L2CAP QoS settings for the app during
- * registration.
- *
- * <p>{@see BluetoothHidDevice}
- */
-public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
-
-    private final int mServiceType;
-    private final int mTokenRate;
-    private final int mTokenBucketSize;
-    private final int mPeakBandwidth;
-    private final int mLatency;
-    private final int mDelayVariation;
-
-    public static final int SERVICE_NO_TRAFFIC = 0x00;
-    public static final int SERVICE_BEST_EFFORT = 0x01;
-    public static final int SERVICE_GUARANTEED = 0x02;
-
-    public static final int MAX = (int) 0xffffffff;
-
-    /**
-     * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
-     * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and
-     * Appendix D for parameters.
-     *
-     * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT
-     * @param tokenRate L2CAP token rate, default = 0
-     * @param tokenBucketSize L2CAP token bucket size, default = 0
-     * @param peakBandwidth L2CAP peak bandwidth, default = 0
-     * @param latency L2CAP latency, default = MAX
-     * @param delayVariation L2CAP delay variation, default = MAX
-     */
-    public BluetoothHidDeviceAppQosSettings(
-            int serviceType,
-            int tokenRate,
-            int tokenBucketSize,
-            int peakBandwidth,
-            int latency,
-            int delayVariation) {
-        mServiceType = serviceType;
-        mTokenRate = tokenRate;
-        mTokenBucketSize = tokenBucketSize;
-        mPeakBandwidth = peakBandwidth;
-        mLatency = latency;
-        mDelayVariation = delayVariation;
-    }
-
-    public int getServiceType() {
-        return mServiceType;
-    }
-
-    public int getTokenRate() {
-        return mTokenRate;
-    }
-
-    public int getTokenBucketSize() {
-        return mTokenBucketSize;
-    }
-
-    public int getPeakBandwidth() {
-        return mPeakBandwidth;
-    }
-
-    public int getLatency() {
-        return mLatency;
-    }
-
-    public int getDelayVariation() {
-        return mDelayVariation;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHidDeviceAppQosSettings> CREATOR =
-            new Parcelable.Creator<BluetoothHidDeviceAppQosSettings>() {
-
-                @Override
-                public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) {
-
-                    return new BluetoothHidDeviceAppQosSettings(
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt(),
-                            in.readInt());
-                }
-
-                @Override
-                public BluetoothHidDeviceAppQosSettings[] newArray(int size) {
-                    return new BluetoothHidDeviceAppQosSettings[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mServiceType);
-        out.writeInt(mTokenRate);
-        out.writeInt(mTokenBucketSize);
-        out.writeInt(mPeakBandwidth);
-        out.writeInt(mLatency);
-        out.writeInt(mDelayVariation);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
deleted file mode 100644
index 4e1a2aa..0000000
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.EventLog;
-
-
-/**
- * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application.
- *
- * <p>The BluetoothHidDevice framework adds the SDP record during app registration, so that the
- * Android device can be discovered as a Bluetooth HID Device.
- *
- * <p>{@see BluetoothHidDevice}
- */
-public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
-
-    private static final int MAX_DESCRIPTOR_SIZE = 2048;
-
-    private final String mName;
-    private final String mDescription;
-    private final String mProvider;
-    private final byte mSubclass;
-    private final byte[] mDescriptors;
-
-    /**
-     * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
-     *
-     * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param subclass Subclass of this Bluetooth HID device. See <a
-     *     href="www.usb.org/developers/hidpage/HID1_11.pdf">
-     *     www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a>
-     * @param descriptors Descriptors of this Bluetooth HID device. See <a
-     *     href="www.usb.org/developers/hidpage/HID1_11.pdf">
-     *     www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes.
-     */
-    public BluetoothHidDeviceAppSdpSettings(
-            String name, String description, String provider, byte subclass, byte[] descriptors) {
-        mName = name;
-        mDescription = description;
-        mProvider = provider;
-        mSubclass = subclass;
-
-        if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) {
-            EventLog.writeEvent(0x534e4554, "119819889", -1, "");
-            throw new IllegalArgumentException("descriptors must be not null and shorter than "
-                    + MAX_DESCRIPTOR_SIZE);
-        }
-        mDescriptors = descriptors.clone();
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public String getDescription() {
-        return mDescription;
-    }
-
-    public String getProvider() {
-        return mProvider;
-    }
-
-    public byte getSubclass() {
-        return mSubclass;
-    }
-
-    public byte[] getDescriptors() {
-        return mDescriptors;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHidDeviceAppSdpSettings> CREATOR =
-            new Parcelable.Creator<BluetoothHidDeviceAppSdpSettings>() {
-
-                @Override
-                public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) {
-
-                    return new BluetoothHidDeviceAppSdpSettings(
-                            in.readString(),
-                            in.readString(),
-                            in.readString(),
-                            in.readByte(),
-                            in.createByteArray());
-                }
-
-                @Override
-                public BluetoothHidDeviceAppSdpSettings[] newArray(int size) {
-                    return new BluetoothHidDeviceAppSdpSettings[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mName);
-        out.writeString(mDescription);
-        out.writeString(mProvider);
-        out.writeByte(mSubclass);
-        out.writeByteArray(mDescriptors);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
deleted file mode 100644
index ecbeddf..0000000
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ /dev/null
@@ -1,831 +0,0 @@
-/*
- * Copyright (C) 2011 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-
-/**
- * This class provides the public APIs to control the Bluetooth Input
- * Device Profile.
- *
- * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHidHost proxy object.
- *
- * <p>Each method is protected with its appropriate permission.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothHidHost implements BluetoothProfile {
-    private static final String TAG = "BluetoothHidHost";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Input
-     * Device profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @SuppressLint("ActionValue")
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PROTOCOL_MODE_CHANGED =
-            "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_HANDSHAKE =
-            "android.bluetooth.input.profile.action.HANDSHAKE";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_REPORT =
-            "android.bluetooth.input.profile.action.REPORT";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_VIRTUAL_UNPLUG_STATUS =
-            "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS";
-
-    /**
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_IDLE_TIME_CHANGED =
-            "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED";
-
-    /**
-     * Return codes for the connect and disconnect Bluez / Dbus calls.
-     *
-     * @hide
-     */
-    public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
-
-    /**
-     * @hide
-     */
-    public static final int INPUT_OPERATION_SUCCESS = 5004;
-
-    /**
-     * @hide
-     */
-    public static final int PROTOCOL_REPORT_MODE = 0;
-
-    /**
-     * @hide
-     */
-    public static final int PROTOCOL_BOOT_MODE = 1;
-
-    /**
-     * @hide
-     */
-    public static final int PROTOCOL_UNSUPPORTED_MODE = 255;
-
-    /*  int reportType, int reportType, int bufferSize */
-    /**
-     * @hide
-     */
-    public static final byte REPORT_TYPE_INPUT = 1;
-
-    /**
-     * @hide
-     */
-    public static final byte REPORT_TYPE_OUTPUT = 2;
-
-    /**
-     * @hide
-     */
-    public static final byte REPORT_TYPE_FEATURE = 3;
-
-    /**
-     * @hide
-     */
-    public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0;
-
-    /**
-     * @hide
-     */
-    public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1;
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_PROTOCOL_MODE =
-            "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT_TYPE =
-            "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT_ID =
-            "android.bluetooth.BluetoothHidHost.extra.REPORT_ID";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT_BUFFER_SIZE =
-            "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_VIRTUAL_UNPLUG_STATUS =
-            "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS";
-
-    /**
-     * @hide
-     */
-    public static final String EXTRA_IDLE_TIME =
-            "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST,
-                    "BluetoothHidHost", IBluetoothHidHost.class.getName()) {
-                @Override
-                public IBluetoothHidHost getServiceInterface(IBinder service) {
-                    return IBluetoothHidHost.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothHidHost proxy object for interacting with the local
-     * Bluetooth Service which handles the InputDevice profile
-     */
-    /* package */ BluetoothHidHost(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /*package*/ void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothHidHost getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> The system supports connection to multiple input devices.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothHidHost service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothHidHost service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        final IBluetoothHidHost service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        if (device == null) {
-            throw new IllegalArgumentException("device must not be null");
-        }
-        final IBluetoothHidHost service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    /**
-     * Initiate virtual unplug for a HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean virtualUnplug(BluetoothDevice device) {
-        if (DBG) log("virtualUnplug(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.virtualUnplug(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Get_Protocol_Mode command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getProtocolMode(BluetoothDevice device) {
-        if (VDBG) log("getProtocolMode(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getProtocolMode(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Set_Protocol_Mode command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
-        if (DBG) log("setProtocolMode(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setProtocolMode(device, protocolMode, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Get_Report command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param reportType Report type
-     * @param reportId Report ID
-     * @param bufferSize Report receiving buffer size
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
-            int bufferSize) {
-        if (VDBG) {
-            log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
-                    + "bufferSize=" + bufferSize);
-        }
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getReport(device, reportType, reportId, bufferSize, mAttributionSource,
-                        recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Set_Report command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param reportType Report type
-     * @param report Report receiving buffer size
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setReport(BluetoothDevice device, byte reportType, String report) {
-        if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setReport(device, reportType, report, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Send_Data command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param report Report to send
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean sendData(BluetoothDevice device, String report) {
-        if (DBG) log("sendData(" + device + "), report=" + report);
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendData(device, report, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Get_Idle_Time command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean getIdleTime(BluetoothDevice device) {
-        if (DBG) log("getIdletime(" + device + ")");
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getIdleTime(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send Set_Idle_Time command to the connected HID input device.
-     *
-     * @param device Remote Bluetooth Device
-     * @param idleTime Idle time to be set on HID Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
-        if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
-        final IBluetoothHidHost service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setIdleTime(device, idleTime, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
deleted file mode 100644
index 95f9229..0000000
--- a/core/java/android/bluetooth/BluetoothInputStream.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.SuppressLint;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * BluetoothInputStream.
- *
- * Used to write to a Bluetooth socket.
- *
- * @hide
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-/*package*/ final class BluetoothInputStream extends InputStream {
-    private BluetoothSocket mSocket;
-
-    /*package*/ BluetoothInputStream(BluetoothSocket s) {
-        mSocket = s;
-    }
-
-    /**
-     * Return number of bytes available before this stream will block.
-     */
-    public int available() throws IOException {
-        return mSocket.available();
-    }
-
-    public void close() throws IOException {
-        mSocket.close();
-    }
-
-    /**
-     * Reads a single byte from this stream and returns it as an integer in the
-     * range from 0 to 255. Returns -1 if the end of the stream has been
-     * reached. Blocks until one byte has been read, the end of the source
-     * stream is detected or an exception is thrown.
-     *
-     * @return the byte read or -1 if the end of stream has been reached.
-     * @throws IOException if the stream is closed or another IOException occurs.
-     * @since Android 1.5
-     */
-    public int read() throws IOException {
-        byte[] b = new byte[1];
-        int ret = mSocket.read(b, 0, 1);
-        if (ret == 1) {
-            return (int) b[0] & 0xff;
-        } else {
-            return -1;
-        }
-    }
-
-    /**
-     * Reads at most {@code length} bytes from this stream and stores them in
-     * the byte array {@code b} starting at {@code offset}.
-     *
-     * @param b the byte array in which to store the bytes read.
-     * @param offset the initial position in {@code buffer} to store the bytes read from this
-     * stream.
-     * @param length the maximum number of bytes to store in {@code b}.
-     * @return the number of bytes actually read or -1 if the end of the stream has been reached.
-     * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code length < 0}, or if {@code
-     * offset + length} is greater than the length of {@code b}.
-     * @throws IOException if the stream is closed or another IOException occurs.
-     * @since Android 1.5
-     */
-    public int read(byte[] b, int offset, int length) throws IOException {
-        if (b == null) {
-            throw new NullPointerException("byte array is null");
-        }
-        if ((offset | length) < 0 || length > b.length - offset) {
-            throw new ArrayIndexOutOfBoundsException("invalid offset or length");
-        }
-        return mSocket.read(b, offset, length);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
deleted file mode 100644
index 15db686..0000000
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ /dev/null
@@ -1,829 +0,0 @@
-/*
- * Copyright 2020 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the LeAudio profile.
- *
- * <p>BluetoothLeAudio is a proxy object for controlling the Bluetooth LE Audio
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothLeAudio proxy object.
- *
- * <p> Android only supports one set of connected Bluetooth LeAudio device at a time. Each
- * method is protected with its appropriate permission.
- */
-public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
-    private static final String TAG = "BluetoothLeAudio";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /**
-     * Intent used to broadcast the change in connection state of the LeAudio
-     * profile. Please note that in the binaural case, there will be two different LE devices for
-     * the left and right side and each device will have their own connection state changes.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
-
-    /**
-     * Intent used to broadcast the selection of a connected device as active.
-     *
-     * <p>This intent will have one extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";
-
-    /**
-     * Intent used to broadcast group node status information.
-     *
-     * <p>This intent will have 3 extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED";
-
-
-    /**
-     * Intent used to broadcast group status information.
-     *
-     * <p>This intent will have 4 extra:
-     * <ul>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
-     * be null if no device is active. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status. </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED";
-
-    /**
-     * Intent used to broadcast group audio configuration changed information.
-     *
-     * <p>This intent will have 5 extra:
-     * <ul>
-     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask. </li>
-     * <li> {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned
-     * Numbers </li>
-     * <li> {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned
-     * Numbers </li>
-     * <li> {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per
-     * Bluetooth Assigned Numbers </li>
-     * </ul>
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LE_AUDIO_CONF_CHANGED =
-            "android.bluetooth.action.LE_AUDIO_CONF_CHANGED";
-
-    /**
-     * Indicates unspecified audio content.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_UNSPECIFIED = 0x0001;
-
-    /**
-     * Indicates conversation between humans as, for example, in telephony or video calls.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002;
-
-    /**
-     * Indicates media as, for example, in music, public radio, podcast or video soundtrack.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_MEDIA = 0x0004;
-
-    /**
-     * Indicates instructional audio as, for example, in navigation, traffic announcements
-     * or user guidance.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_INSTRUCTIONAL = 0x0008;
-
-    /**
-     * Indicates attention seeking audio as, for example, in beeps signalling arrival of a message
-     * or keyboard clicks.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_ATTENTION_SEEKING = 0x0010;
-
-    /**
-     * Indicates immediate alerts as, for example, in a low battery alarm, timer expiry or alarm
-     * clock.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_IMMEDIATE_ALERT = 0x0020;
-
-    /**
-     * Indicates man machine communication as, for example, with voice recognition or virtual
-     * assistant.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_MAN_MACHINE = 0x0040;
-
-    /**
-     * Indicates emergency alerts as, for example, with fire alarms or other urgent alerts.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_EMERGENCY_ALERT = 0x0080;
-
-    /**
-     * Indicates ringtone as in a call alert.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_RINGTONE = 0x0100;
-
-    /**
-     * Indicates audio associated with a television program and/or with metadata conforming to the
-     * Bluetooth Broadcast TV profile.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_TV = 0x0200;
-
-    /**
-     * Indicates audio associated with a low latency live audio stream.
-     *
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_LIVE = 0x0400;
-
-    /**
-     * Indicates audio associated with a video game stream.
-     * @hide
-     */
-    public static final int CONTEXT_TYPE_GAME = 0x0800;
-
-    /**
-     * This represents an invalid group ID.
-     *
-     * @hide
-     */
-    public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
-
-    /**
-     * Contains group id.
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_GROUP_ID =
-            "android.bluetooth.extra.LE_AUDIO_GROUP_ID";
-
-    /**
-     * Contains group node status, can be any of
-     * <p>
-     * <ul>
-     * <li> {@link #GROUP_NODE_ADDED} </li>
-     * <li> {@link #GROUP_NODE_REMOVED} </li>
-     * </ul>
-     * <p>
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS =
-            "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS";
-
-    /**
-     * Contains group status, can be any of
-     *
-     * <p>
-     * <ul>
-     * <li> {@link #GROUP_STATUS_ACTIVE} </li>
-     * <li> {@link #GROUP_STATUS_INACTIVE} </li>
-     * </ul>
-     * <p>
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_GROUP_STATUS =
-            "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS";
-
-    /**
-     * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source.
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_DIRECTION =
-            "android.bluetooth.extra.LE_AUDIO_DIRECTION";
-
-    /**
-     * Contains source location as per Bluetooth Assigned Numbers
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION =
-            "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION";
-
-    /**
-     * Contains sink location as per Bluetooth Assigned Numbers
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_SINK_LOCATION =
-            "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION";
-
-    /**
-     * Contains available context types for group as per Bluetooth Assigned Numbers
-     * @hide
-     */
-    public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS =
-            "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS";
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    /**
-     * Indicating that group is Active ( Audio device is available )
-     * @hide
-     */
-    public static final int GROUP_STATUS_ACTIVE = IBluetoothLeAudio.GROUP_STATUS_ACTIVE;
-
-    /**
-     * Indicating that group is Inactive ( Audio device is not available )
-     * @hide
-     */
-    public static final int GROUP_STATUS_INACTIVE = IBluetoothLeAudio.GROUP_STATUS_INACTIVE;
-
-    /**
-     * Indicating that node has been added to the group.
-     * @hide
-     */
-    public static final int GROUP_NODE_ADDED = IBluetoothLeAudio.GROUP_NODE_ADDED;
-
-    /**
-     * Indicating that node has been removed from the group.
-     * @hide
-     */
-    public static final int GROUP_NODE_REMOVED = IBluetoothLeAudio.GROUP_NODE_REMOVED;
-
-    private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio",
-                    IBluetoothLeAudio.class.getName()) {
-                @Override
-                public IBluetoothLeAudio getServiceInterface(IBinder service) {
-                    return IBluetoothLeAudio.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothLeAudio proxy object for interacting with the local
-     * Bluetooth LeAudio service.
-     */
-    /* package */ BluetoothLeAudio(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    /**
-     * @hide
-     */
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothLeAudio getService() {
-        return mProfileConnector.getService();
-    }
-
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean connect(@Nullable BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(@Nullable BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothLeAudio service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
-            @NonNull int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothLeAudio service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Select a connected device as active.
-     *
-     * The active device selection is per profile. An active device's
-     * purpose is profile-specific. For example, LeAudio audio
-     * streaming is to the active LeAudio device. If a remote device
-     * is not connected, it cannot be selected as active.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is not connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that the
-     * {@link #ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
-     * with the active device.
-     *
-     *
-     * @param device the remote Bluetooth device. Could be null to clear
-     * the active device and stop streaming audio to a Bluetooth device.
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
-        if (DBG) log("setActiveDevice(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setActiveDevice(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connected LeAudio devices that are active
-     *
-     * @return the list of active devices. Returns empty list on error.
-     * @hide
-     */
-    @NonNull
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getActiveDevices() {
-        if (VDBG) log("getActiveDevice()");
-        final IBluetoothLeAudio service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getActiveDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get device group id. Devices with same group id belong to same group (i.e left and right
-     * earbud)
-     * @param device LE Audio capable device
-     * @return group id that this device currently belongs to
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getGroupId(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getGroupId()");
-        final IBluetoothLeAudio service = getService();
-        final int defaultValue = GROUP_ID_INVALID;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getGroupId(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set volume for the streaming devices
-     *
-     * @param volume volume to set
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED})
-    public void setVolume(int volume) {
-        if (VDBG) log("setVolume(vol: " + volume + " )");
-        final IBluetoothLeAudio service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setVolume(volume, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Add device to the given group.
-     * @param group_id group ID the device is being added to
-     * @param device the active device
-     * @return true on success, otherwise false
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED
-    })
-    public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) {
-        if (VDBG) log("groupAddNode()");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.groupAddNode(group_id, device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Remove device from a given group.
-     * @param group_id group ID the device is being removed from
-     * @param device the active device
-     * @return true on success, otherwise false
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED
-    })
-    public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) {
-        if (VDBG) log("groupRemoveNode()");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.groupRemoveNode(group_id, device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothLeAudio service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothLeAudio service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-
-    /**
-     * Helper for converting a state to a string.
-     *
-     * For debug use only - strings are not internationalized.
-     *
-     * @hide
-     */
-    public static String stateToString(int state) {
-        switch (state) {
-            case STATE_DISCONNECTED:
-                return "disconnected";
-            case STATE_CONNECTING:
-                return "connecting";
-            case STATE_CONNECTED:
-                return "connected";
-            case STATE_DISCONNECTING:
-                return "disconnecting";
-            default:
-                return "<unknown state " + state + ">";
-        }
-    }
-
-    private boolean isValidDevice(@Nullable BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
deleted file mode 100644
index dcaf4b6..0000000
--- a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Represents the codec configuration for a Bluetooth LE Audio source device.
- * <p>Contains the source codec type.
- * <p>The source codec type values are the same as those supported by the
- * device hardware.
- *
- * {@see BluetoothLeAudioCodecConfig}
- */
-public final class BluetoothLeAudioCodecConfig {
-    // Add an entry for each source codec here.
-
-    /** @hide */
-    @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
-            SOURCE_CODEC_TYPE_LC3,
-            SOURCE_CODEC_TYPE_INVALID
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SourceCodecType {};
-
-    public static final int SOURCE_CODEC_TYPE_LC3 = 0;
-    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
-
-    /**
-     * Represents the count of valid source codec types. Can be accessed via
-     * {@link #getMaxCodecType}.
-     */
-    private static final int SOURCE_CODEC_TYPE_MAX = 1;
-
-    private final @SourceCodecType int mCodecType;
-
-    /**
-     * Creates a new BluetoothLeAudioCodecConfig.
-     *
-     * @param codecType the source codec type
-     */
-    private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType) {
-        mCodecType = codecType;
-    }
-
-    @Override
-    public String toString() {
-        return "{codecName:" + getCodecName() + "}";
-    }
-
-    /**
-     * Gets the codec type.
-     *
-     * @return the codec type
-     */
-    public @SourceCodecType int getCodecType() {
-        return mCodecType;
-    }
-
-    /**
-     * Returns the valid codec types count.
-     */
-    public static int getMaxCodecType() {
-        return SOURCE_CODEC_TYPE_MAX;
-    }
-
-    /**
-     * Gets the codec name.
-     *
-     * @return the codec name
-     */
-    public @NonNull String getCodecName() {
-        switch (mCodecType) {
-            case SOURCE_CODEC_TYPE_LC3:
-                return "LC3";
-            case SOURCE_CODEC_TYPE_INVALID:
-                return "INVALID CODEC";
-            default:
-                break;
-        }
-        return "UNKNOWN CODEC(" + mCodecType + ")";
-    }
-
-    /**
-     * Builder for {@link BluetoothLeAudioCodecConfig}.
-     * <p> By default, the codec type will be set to
-     * {@link BluetoothLeAudioCodecConfig#SOURCE_CODEC_TYPE_INVALID}
-     */
-    public static final class Builder {
-        private int mCodecType = BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID;
-
-        /**
-         * Set codec type for Bluetooth codec config.
-         *
-         * @param codecType of this codec
-         * @return the same Builder instance
-         */
-        public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
-            mCodecType = codecType;
-            return this;
-        }
-
-        /**
-         * Build {@link BluetoothLeAudioCodecConfig}.
-         * @return new BluetoothLeAudioCodecConfig built
-         */
-        public @NonNull BluetoothLeAudioCodecConfig build() {
-            return new BluetoothLeAudioCodecConfig(mCodecType);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcast.java b/core/java/android/bluetooth/BluetoothLeBroadcast.java
deleted file mode 100644
index fed9f91..0000000
--- a/core/java/android/bluetooth/BluetoothLeBroadcast.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * This class provides the public APIs to control the Bluetooth LE Broadcast Source profile.
- *
- * <p>BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast
- * Source Service via IPC. Use {@link BluetoothAdapter#getProfileProxy}
- * to get the BluetoothLeBroadcast proxy object.
- *
- * @hide
- */
-public final class BluetoothLeBroadcast implements BluetoothProfile {
-    private static final String TAG = "BluetoothLeBroadcast";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Constants used by the LE Audio Broadcast profile for the Broadcast state
-     *
-     * @hide
-     */
-    @IntDef(prefix = {"LE_AUDIO_BROADCAST_STATE_"}, value = {
-      LE_AUDIO_BROADCAST_STATE_DISABLED,
-      LE_AUDIO_BROADCAST_STATE_ENABLING,
-      LE_AUDIO_BROADCAST_STATE_ENABLED,
-      LE_AUDIO_BROADCAST_STATE_DISABLING,
-      LE_AUDIO_BROADCAST_STATE_PLAYING,
-      LE_AUDIO_BROADCAST_STATE_NOT_PLAYING
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioBroadcastState {}
-
-    /**
-     * Indicates that LE Audio Broadcast mode is currently disabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10;
-
-    /**
-     * Indicates that LE Audio Broadcast mode is being enabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11;
-
-    /**
-     * Indicates that LE Audio Broadcast mode is currently enabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_ENABLED = 12;
-    /**
-     * Indicates that LE Audio Broadcast mode is being disabled
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_DISABLING = 13;
-
-    /**
-     * Indicates that an LE Audio Broadcast mode is currently playing
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14;
-
-    /**
-     * Indicates that LE Audio Broadcast is currently not playing
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15;
-
-    /**
-     * Constants used by the LE Audio Broadcast profile for encryption key length
-     *
-     * @hide
-     */
-    @IntDef(prefix = {"LE_AUDIO_BROADCAST_ENCRYPTION_KEY_"}, value = {
-      LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT,
-      LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioEncryptionKeyLength {}
-
-    /**
-     * Indicates that the LE Audio Broadcast encryption key size is 32 bits.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16;
-
-    /**
-     * Indicates that the LE Audio Broadcast encryption key size is 128 bits.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17;
-
-    /**
-     * Interface for receiving events related to broadcasts
-     */
-    public interface Callback {
-        /**
-         * Called when broadcast state has changed
-         *
-         * @param prevState broadcast state before the change
-         * @param newState broadcast state after the change
-         */
-        @LeAudioBroadcastState
-        void onBroadcastStateChange(int prevState, int newState);
-        /**
-         * Called when encryption key has been updated
-         *
-         * @param success true if the key was updated successfully, false otherwise
-         */
-        void onEncryptionKeySet(boolean success);
-    }
-
-    /**
-     * Create a BluetoothLeBroadcast proxy object for interacting with the local
-     * LE Audio Broadcast Source service.
-     *
-     * @hide
-     */
-    /*package*/ BluetoothLeBroadcast(Context context,
-                                     BluetoothProfile.ServiceListener listener) {
-    }
-
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection
-     *
-     * @throws UnsupportedOperationException
-     *
-     * @hide
-     */
-    @Override
-    public int getConnectionState(BluetoothDevice device) {
-        throw new UnsupportedOperationException(
-                   "LE Audio Broadcasts are not connection-oriented.");
-    }
-
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection
-     *
-     * @throws UnsupportedOperationException
-     *
-     * @hide
-     */
-    @Override
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        throw new UnsupportedOperationException(
-                   "LE Audio Broadcasts are not connection-oriented.");
-    }
-
-    /**
-     * Not supported since LE Audio Broadcasts do not establish a connection
-     *
-     * @throws UnsupportedOperationException
-     *
-     * @hide
-     */
-    @Override
-    public List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException(
-                   "LE Audio Broadcasts are not connection-oriented.");
-    }
-
-    /**
-     * Enable LE Audio Broadcast mode.
-     *
-     * Generates a new broadcast ID and enables sending of encrypted or unencrypted
-     * isochronous PDUs
-     *
-     * @hide
-     */
-    public int enableBroadcastMode() {
-        if (DBG) log("enableBroadcastMode");
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
-    }
-
-    /**
-     * Disable LE Audio Broadcast mode.
-     *
-     * @hide
-     */
-    public int disableBroadcastMode() {
-        if (DBG) log("disableBroadcastMode");
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED;
-    }
-
-    /**
-     * Get the current LE Audio broadcast state
-     *
-     * @hide
-     */
-    @LeAudioBroadcastState
-    public int getBroadcastState() {
-        if (DBG) log("getBroadcastState");
-        return LE_AUDIO_BROADCAST_STATE_DISABLED;
-    }
-
-    /**
-     * Enable LE Audio broadcast encryption
-     *
-     * @param keyLength if useExisting is true, this specifies the length of the key that should
-     *                  be generated
-     * @param useExisting true, if an existing key should be used
-     *                    false, if a new key should be generated
-     *
-     * @hide
-     */
-    @LeAudioEncryptionKeyLength
-    public int enableEncryption(boolean useExisting, int keyLength) {
-        if (DBG) log("enableEncryption useExisting=" + useExisting + " keyLength=" + keyLength);
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED;
-    }
-
-    /**
-     * Disable LE Audio broadcast encryption
-     *
-     * @param removeExisting true, if the existing key should be removed
-     *                       false, otherwise
-     *
-     * @hide
-     */
-    public int disableEncryption(boolean removeExisting) {
-        if (DBG) log("disableEncryption removeExisting=" + removeExisting);
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED;
-    }
-
-    /**
-     * Enable or disable LE Audio broadcast encryption
-     *
-     * @param key use the provided key if non-null, generate a new key if null
-     * @param keyLength 0 if encryption is disabled, 4 bytes (low security),
-     *                  16 bytes (high security)
-     *
-     * @hide
-     */
-    @LeAudioEncryptionKeyLength
-    public int setEncryptionKey(byte[] key, int keyLength) {
-        if (DBG) log("setEncryptionKey key=" + key + " keyLength=" + keyLength);
-        return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED;
-    }
-
-
-    /**
-     * Get the encryption key that was set before
-     *
-     * @return encryption key as a byte array or null if no encryption key was set
-     *
-     * @hide
-     */
-    public byte[] getEncryptionKey() {
-        if (DBG) log("getEncryptionKey");
-        return null;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
deleted file mode 100644
index b866cce..0000000
--- a/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.bluetooth.le.ScanResult;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources is
- * offloaded to a Broadcast Assistant.
- *
- * <p>An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast
- * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant. This
- * is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT server that is
- * part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on the Broadcast
- * Assistant.
- *
- * <p>Once a GATT connection is established between the BASS client and the BASS server, the
- * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast
- * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the
- * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the
- * Assistant about changes such as addition and removal of Broadcast Sources.
- *
- * @hide
- */
-public abstract class BluetoothLeBroadcastAssistantCallback {
-
-    /**
-     * Broadcast Audio Scan Service (BASS) codes returned by a BASS Server
-     *
-     * @hide
-     */
-    @IntDef(
-            prefix = "BASS_STATUS_",
-            value = {
-                BASS_STATUS_SUCCESS,
-                BASS_STATUS_FAILURE,
-                BASS_STATUS_INVALID_GATT_HANDLE,
-                BASS_STATUS_TXN_TIMEOUT,
-                BASS_STATUS_INVALID_SOURCE_ID,
-                BASS_STATUS_COLOCATED_SRC_UNAVAILABLE,
-                BASS_STATUS_INVALID_SOURCE_SELECTED,
-                BASS_STATUS_SOURCE_UNAVAILABLE,
-                BASS_STATUS_DUPLICATE_ADDITION,
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BassStatus {}
-
-    public static final int BASS_STATUS_SUCCESS = 0x00;
-    public static final int BASS_STATUS_FAILURE = 0x01;
-    public static final int BASS_STATUS_INVALID_GATT_HANDLE = 0x02;
-    public static final int BASS_STATUS_TXN_TIMEOUT = 0x03;
-
-    public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04;
-    public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05;
-    public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06;
-    public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07;
-    public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08;
-    public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
-    public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
-
-    /**
-     * Callback invoked when a new LE Audio Broadcast Source is found.
-     *
-     * @param result {@link ScanResult} scan result representing a Broadcast Source
-     */
-    public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {}
-
-    /**
-     * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs)
-     * of an LE Audio Broadcast Source.
-     *
-     * @param source the selected Broadcast Source
-     */
-    public void onBluetoothLeBroadcastSourceSelected(
-            @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
-
-    /**
-     * Callback invoked when the Broadcast Assistant loses synchronization with an LE Audio
-     * Broadcast Source.
-     *
-     * @param source the Broadcast Source with which synchronization was lost
-     */
-    public void onBluetoothLeBroadcastSourceLost(
-            @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
-
-    /**
-     * Callback invoked when a new LE Audio Broadcast Source has been successfully added to the Scan
-     * Delegator (within a Broadcast Sink, for example).
-     *
-     * @param sink Scan Delegator device on which a new Broadcast Source has been added
-     * @param source the added Broadcast Source
-     */
-    public void onBluetoothLeBroadcastSourceAdded(
-            @NonNull BluetoothDevice sink,
-            @NonNull BluetoothLeBroadcastSourceInfo source,
-            @BassStatus int status) {}
-
-    /**
-     * Callback invoked when an existing LE Audio Broadcast Source within a remote Scan Delegator
-     * has been updated.
-     *
-     * @param sink Scan Delegator device on which a Broadcast Source has been updated
-     * @param source the updated Broadcast Source
-     */
-    public void onBluetoothLeBroadcastSourceUpdated(
-            @NonNull BluetoothDevice sink,
-            @NonNull BluetoothLeBroadcastSourceInfo source,
-            @BassStatus int status) {}
-
-    /**
-     * Callback invoked when an LE Audio Broadcast Source has been successfully removed from the
-     * Scan Delegator (within a Broadcast Sink, for example).
-     *
-     * @param sink Scan Delegator device from which a Broadcast Source has been removed
-     * @param source the removed Broadcast Source
-     */
-    public void onBluetoothLeBroadcastSourceRemoved(
-            @NonNull BluetoothDevice sink,
-            @NonNull BluetoothLeBroadcastSourceInfo source,
-            @BassStatus int status) {}
-}
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java b/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java
deleted file mode 100644
index cb47280..0000000
--- a/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java
+++ /dev/null
@@ -1,788 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class represents an LE Audio Broadcast Source and the associated information that is needed
- * by Broadcast Audio Scan Service (BASS) residing on a Scan Delegator.
- *
- * <p>For example, the Scan Delegator on an LE Audio Broadcast Sink can use the information
- * contained within an instance of this class to synchronize with an LE Audio Broadcast Source in
- * order to listen to a Broadcast Audio Stream.
- *
- * <p>BroadcastAssistant has a BASS client which facilitates scanning and discovery of Broadcast
- * Sources on behalf of say a Broadcast Sink. Upon successful discovery of one or more Broadcast
- * sources, this information needs to be communicated to the BASS Server residing within the Scan
- * Delegator on a Broadcast Sink. This is achieved using the Periodic Advertising Synchronization
- * Transfer (PAST) procedure. This procedure uses information contained within an instance of this
- * class.
- *
- * @hide
- */
-public final class BluetoothLeBroadcastSourceInfo implements Parcelable {
-    private static final String TAG = "BluetoothLeBroadcastSourceInfo";
-    private static final boolean DBG = true;
-
-    /**
-     * Constants representing Broadcast Source address types
-     *
-     * @hide
-     */
-    @IntDef(
-            prefix = "LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_",
-            value = {
-                LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC,
-                LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM,
-                LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioBroadcastSourceAddressType {}
-
-    /**
-     * Represents a public address used by an LE Audio Broadcast Source
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC = 0;
-
-    /**
-     * Represents a random address used by an LE Audio Broadcast Source
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM = 1;
-
-    /**
-     * Represents an invalid address used by an LE Audio Broadcast Seurce
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID = 0xFFFF;
-
-    /**
-     * Periodic Advertising Synchronization state
-     *
-     * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast
-     * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast
-     * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the
-     * Periodic Advertising Synchronizaton Transfer (PAST) procedure.
-     *
-     * @hide
-     */
-    @IntDef(
-            prefix = "LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_",
-            value = {
-                LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE,
-                LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ,
-                LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC,
-                LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL,
-                LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioBroadcastSinkPaSyncState {}
-
-    /**
-     * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA)
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE = 0;
-
-    /**
-     * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the
-     * Periodic Advertisements (PA).
-     *
-     * <p>This is also known as scan delegation or scan offloading.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ = 1;
-
-    /**
-     * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA).
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC = 2;
-
-    /**
-     * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements
-     * (PA).
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL = 3;
-
-    /**
-     * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements
-     * (PA) using the Periodic Advertisements Synchronization Transfert (PAST) procedure.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST = 4;
-
-    /**
-     * Indicates that the Broadcast Sink synchornization state is invalid.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID = 0xFFFF;
-
-    /** @hide */
-    @IntDef(
-            prefix = "LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_",
-            value = {
-                LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED,
-                LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioBroadcastSinkAudioSyncState {}
-
-    /**
-     * Indicates that the Broadcast Sink is not synchronized with a Broadcast Audio Stream.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED = 0;
-
-    /**
-     * Indicates that the Broadcast Sink is synchronized with a Broadcast Audio Stream.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED = 1;
-
-    /**
-     * Indicates that the Broadcast Sink audio synchronization state is invalid.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID = 0xFFFF;
-
-    /** @hide */
-    @IntDef(
-            prefix = "LE_AUDIO_BROADCAST_SINK_ENC_STATE_",
-            value = {
-                LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED,
-                LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED,
-                LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING,
-                LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LeAudioBroadcastSinkEncryptionState {}
-
-    /**
-     * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED = 0;
-
-    /**
-     * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with the audio
-     * stream.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED = 1;
-
-    /**
-     * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING = 2;
-
-    /**
-     * Indicates that the Broadcast Sink is unable to decrypt an audio stream due to an incorrect
-     * Broadcast Code
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE = 3;
-
-    /**
-     * Indicates that the Broadcast Sink encryption state is invalid.
-     *
-     * @hide
-     */
-    public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID = 0xFF;
-
-    /**
-     * Represents an invalid LE Audio Broadcast Source ID
-     *
-     * @hide
-     */
-    public static final byte LE_AUDIO_BROADCAST_SINK_INVALID_SOURCE_ID = (byte) 0x00;
-
-    /**
-     * Represents an invalid Broadcast ID of a Broadcast Source
-     *
-     * @hide
-     */
-    public static final int INVALID_BROADCAST_ID = 0xFFFFFF;
-
-    private byte mSourceId;
-    private @LeAudioBroadcastSourceAddressType int mSourceAddressType;
-    private BluetoothDevice mSourceDevice;
-    private byte mSourceAdvSid;
-    private int mBroadcastId;
-    private @LeAudioBroadcastSinkPaSyncState int mPaSyncState;
-    private @LeAudioBroadcastSinkEncryptionState int mEncryptionStatus;
-    private @LeAudioBroadcastSinkAudioSyncState int mAudioSyncState;
-    private byte[] mBadBroadcastCode;
-    private byte mNumSubGroups;
-    private Map<Integer, Integer> mSubgroupBisSyncState = new HashMap<Integer, Integer>();
-    private Map<Integer, byte[]> mSubgroupMetadata = new HashMap<Integer, byte[]>();
-
-    private String mBroadcastCode;
-    private static final int BIS_NO_PREF = 0xFFFFFFFF;
-    private static final int BROADCAST_CODE_SIZE = 16;
-
-    /**
-     * Constructor to create an Empty object of {@link BluetoothLeBroadcastSourceInfo } with the
-     * given Source Id.
-     *
-     * <p>This is mainly used to represent the Empty Broadcast Source entries
-     *
-     * @param sourceId Source Id for this Broadcast Source info object
-     * @hide
-     */
-    public BluetoothLeBroadcastSourceInfo(byte sourceId) {
-        mSourceId = sourceId;
-        mSourceAddressType = LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID;
-        mSourceDevice = null;
-        mSourceAdvSid = (byte) 0x00;
-        mBroadcastId = INVALID_BROADCAST_ID;
-        mPaSyncState = LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID;
-        mAudioSyncState = LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID;
-        mEncryptionStatus = LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID;
-        mBadBroadcastCode = null;
-        mNumSubGroups = 0;
-        mBroadcastCode = null;
-    }
-
-    /*package*/ BluetoothLeBroadcastSourceInfo(
-            byte sourceId,
-            @LeAudioBroadcastSourceAddressType int addressType,
-            @NonNull BluetoothDevice device,
-            byte advSid,
-            int broadcastId,
-            @LeAudioBroadcastSinkPaSyncState int paSyncstate,
-            @LeAudioBroadcastSinkEncryptionState int encryptionStatus,
-            @LeAudioBroadcastSinkAudioSyncState int audioSyncstate,
-            @Nullable byte[] badCode,
-            byte numSubGroups,
-            @NonNull Map<Integer, Integer> bisSyncState,
-            @Nullable Map<Integer, byte[]> subgroupMetadata,
-            @NonNull String broadcastCode) {
-        mSourceId = sourceId;
-        mSourceAddressType = addressType;
-        mSourceDevice = device;
-        mSourceAdvSid = advSid;
-        mBroadcastId = broadcastId;
-        mPaSyncState = paSyncstate;
-        mEncryptionStatus = encryptionStatus;
-        mAudioSyncState = audioSyncstate;
-
-        if (badCode != null && badCode.length != 0) {
-            mBadBroadcastCode = new byte[badCode.length];
-            System.arraycopy(badCode, 0, mBadBroadcastCode, 0, badCode.length);
-        }
-        mNumSubGroups = numSubGroups;
-        mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState);
-        mSubgroupMetadata = new HashMap<Integer, byte[]>(subgroupMetadata);
-        mBroadcastCode = broadcastCode;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothLeBroadcastSourceInfo) {
-            BluetoothLeBroadcastSourceInfo other = (BluetoothLeBroadcastSourceInfo) o;
-            return (other.mSourceId == mSourceId
-                    && other.mSourceAddressType == mSourceAddressType
-                    && other.mSourceDevice == mSourceDevice
-                    && other.mSourceAdvSid == mSourceAdvSid
-                    && other.mBroadcastId == mBroadcastId
-                    && other.mPaSyncState == mPaSyncState
-                    && other.mEncryptionStatus == mEncryptionStatus
-                    && other.mAudioSyncState == mAudioSyncState
-                    && Arrays.equals(other.mBadBroadcastCode, mBadBroadcastCode)
-                    && other.mNumSubGroups == mNumSubGroups
-                    && mSubgroupBisSyncState.equals(other.mSubgroupBisSyncState)
-                    && mSubgroupMetadata.equals(other.mSubgroupMetadata)
-                    && other.mBroadcastCode == mBroadcastCode);
-        }
-        return false;
-    }
-
-    /**
-     * Checks if an instance of {@link BluetoothLeBroadcastSourceInfo} is empty.
-     *
-     * @hide
-     */
-    public boolean isEmpty() {
-        boolean ret = false;
-        if (mSourceAddressType == LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID
-                && mSourceDevice == null
-                && mSourceAdvSid == (byte) 0
-                && mPaSyncState == LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID
-                && mEncryptionStatus == LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID
-                && mAudioSyncState == LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID
-                && mBadBroadcastCode == null
-                && mNumSubGroups == 0
-                && mSubgroupBisSyncState.size() == 0
-                && mSubgroupMetadata.size() == 0
-                && mBroadcastCode == null) {
-            ret = true;
-        }
-        return ret;
-    }
-
-    /**
-     * Compares an instance of {@link BluetoothLeBroadcastSourceInfo} with the provided instance.
-     *
-     * @hide
-     */
-    public boolean matches(BluetoothLeBroadcastSourceInfo srcInfo) {
-        boolean ret = false;
-        if (srcInfo == null) {
-            ret = false;
-        } else {
-            if (mSourceDevice == null) {
-                if (mSourceAdvSid == srcInfo.getAdvertisingSid()
-                        && mSourceAddressType == srcInfo.getAdvAddressType()) {
-                    ret = true;
-                }
-            } else {
-                if (mSourceDevice.equals(srcInfo.getSourceDevice())
-                        && mSourceAdvSid == srcInfo.getAdvertisingSid()
-                        && mSourceAddressType == srcInfo.getAdvAddressType()
-                        && mBroadcastId == srcInfo.getBroadcastId()) {
-                    ret = true;
-                }
-            }
-        }
-        return ret;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(
-                mSourceId,
-                mSourceAddressType,
-                mSourceDevice,
-                mSourceAdvSid,
-                mBroadcastId,
-                mPaSyncState,
-                mEncryptionStatus,
-                mAudioSyncState,
-                mBadBroadcastCode,
-                mNumSubGroups,
-                mSubgroupBisSyncState,
-                mSubgroupMetadata,
-                mBroadcastCode);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public String toString() {
-        return "{BluetoothLeBroadcastSourceInfo : mSourceId"
-                + mSourceId
-                + " addressType: "
-                + mSourceAddressType
-                + " sourceDevice: "
-                + mSourceDevice
-                + " mSourceAdvSid:"
-                + mSourceAdvSid
-                + " mBroadcastId:"
-                + mBroadcastId
-                + " mPaSyncState:"
-                + mPaSyncState
-                + " mEncryptionStatus:"
-                + mEncryptionStatus
-                + " mAudioSyncState:"
-                + mAudioSyncState
-                + " mBadBroadcastCode:"
-                + mBadBroadcastCode
-                + " mNumSubGroups:"
-                + mNumSubGroups
-                + " mSubgroupBisSyncState:"
-                + mSubgroupBisSyncState
-                + " mSubgroupMetadata:"
-                + mSubgroupMetadata
-                + " mBroadcastCode:"
-                + mBroadcastCode
-                + "}";
-    }
-
-    /**
-     * Get the Source Id
-     *
-     * @return byte representing the Source Id, {@link
-     *     #LE_AUDIO_BROADCAST_ASSISTANT_INVALID_SOURCE_ID} if invalid
-     * @hide
-     */
-    public byte getSourceId() {
-        return mSourceId;
-    }
-
-    /**
-     * Set the Source Id
-     *
-     * @param sourceId source Id
-     * @hide
-     */
-    public void setSourceId(byte sourceId) {
-        mSourceId = sourceId;
-    }
-
-    /**
-     * Set the Broadcast Source device
-     *
-     * @param sourceDevice the Broadcast Source BluetoothDevice
-     * @hide
-     */
-    public void setSourceDevice(@NonNull BluetoothDevice sourceDevice) {
-        mSourceDevice = sourceDevice;
-    }
-
-    /**
-     * Get the Broadcast Source BluetoothDevice
-     *
-     * @return Broadcast Source BluetoothDevice
-     * @hide
-     */
-    public @NonNull BluetoothDevice getSourceDevice() {
-        return mSourceDevice;
-    }
-
-    /**
-     * Set the address type of the Broadcast Source advertisements
-     *
-     * @hide
-     */
-    public void setAdvAddressType(@LeAudioBroadcastSourceAddressType int addressType) {
-        mSourceAddressType = addressType;
-    }
-
-    /**
-     * Get the address type used by advertisements from the Broadcast Source.
-     * BluetoothLeBroadcastSourceInfo Object
-     *
-     * @hide
-     */
-    @LeAudioBroadcastSourceAddressType
-    public int getAdvAddressType() {
-        return mSourceAddressType;
-    }
-
-    /**
-     * Set the advertising SID of the Broadcast Source advertisement.
-     *
-     * @param advSid advertising SID of the Broadcast Source
-     * @hide
-     */
-    public void setAdvertisingSid(byte advSid) {
-        mSourceAdvSid = advSid;
-    }
-
-    /**
-     * Get the advertising SID of the Broadcast Source advertisement.
-     *
-     * @return advertising SID of the Broadcast Source
-     * @hide
-     */
-    public byte getAdvertisingSid() {
-        return mSourceAdvSid;
-    }
-
-    /**
-     * Get the Broadcast ID of the Broadcast Source.
-     *
-     * @return broadcast ID
-     * @hide
-     */
-    public int getBroadcastId() {
-        return mBroadcastId;
-    }
-
-    /**
-     * Set the Periodic Advertising (PA) Sync State.
-     *
-     * @hide
-     */
-    /*package*/ void setPaSyncState(@LeAudioBroadcastSinkPaSyncState int paSyncState) {
-        mPaSyncState = paSyncState;
-    }
-
-    /**
-     * Get the Periodic Advertising (PA) Sync State
-     *
-     * @hide
-     */
-    public @LeAudioBroadcastSinkPaSyncState int getMetadataSyncState() {
-        return mPaSyncState;
-    }
-
-    /**
-     * Set the audio sync state
-     *
-     * @hide
-     */
-    /*package*/ void setAudioSyncState(@LeAudioBroadcastSinkAudioSyncState int audioSyncState) {
-        mAudioSyncState = audioSyncState;
-    }
-
-    /**
-     * Get the audio sync state
-     *
-     * @hide
-     */
-    public @LeAudioBroadcastSinkAudioSyncState int getAudioSyncState() {
-        return mAudioSyncState;
-    }
-
-    /**
-     * Set the encryption status
-     *
-     * @hide
-     */
-    /*package*/ void setEncryptionStatus(
-            @LeAudioBroadcastSinkEncryptionState int encryptionStatus) {
-        mEncryptionStatus = encryptionStatus;
-    }
-
-    /**
-     * Get the encryption status
-     *
-     * @hide
-     */
-    public @LeAudioBroadcastSinkEncryptionState int getEncryptionStatus() {
-        return mEncryptionStatus;
-    }
-
-    /**
-     * Get the incorrect broadcast code that the Scan delegator used to decrypt the Broadcast Audio
-     * Stream and failed.
-     *
-     * <p>This code is valid only if {@link #getEncryptionStatus} returns {@link
-     * #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE}
-     *
-     * @return byte array containing bad broadcast value, null if the current encryption status is
-     *     not {@link #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE}
-     * @hide
-     */
-    public @Nullable byte[] getBadBroadcastCode() {
-        return mBadBroadcastCode;
-    }
-
-    /**
-     * Get the number of subgroups.
-     *
-     * @return number of subgroups
-     * @hide
-     */
-    public byte getNumberOfSubGroups() {
-        return mNumSubGroups;
-    }
-
-    public @NonNull Map<Integer, Integer> getSubgroupBisSyncState() {
-        return mSubgroupBisSyncState;
-    }
-
-    public void setSubgroupBisSyncState(@NonNull Map<Integer, Integer> bisSyncState) {
-        mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState);
-    }
-
-    /*package*/ void setBroadcastCode(@NonNull String broadcastCode) {
-        mBroadcastCode = broadcastCode;
-    }
-
-    /**
-     * Get the broadcast code
-     *
-     * @return
-     * @hide
-     */
-    public @NonNull String getBroadcastCode() {
-        return mBroadcastCode;
-    }
-
-    /**
-     * Set the broadcast ID
-     *
-     * @param broadcastId broadcast ID of the Broadcast Source
-     * @hide
-     */
-    public void setBroadcastId(int broadcastId) {
-        mBroadcastId = broadcastId;
-    }
-
-    private void writeSubgroupBisSyncStateToParcel(
-            @NonNull Parcel dest, @NonNull Map<Integer, Integer> subgroupBisSyncState) {
-        dest.writeInt(subgroupBisSyncState.size());
-        for (Map.Entry<Integer, Integer> entry : subgroupBisSyncState.entrySet()) {
-            dest.writeInt(entry.getKey());
-            dest.writeInt(entry.getValue());
-        }
-    }
-
-    private static void readSubgroupBisSyncStateFromParcel(
-            @NonNull Parcel in, @NonNull Map<Integer, Integer> subgroupBisSyncState) {
-        int size = in.readInt();
-
-        for (int i = 0; i < size; i++) {
-            Integer key = in.readInt();
-            Integer value = in.readInt();
-            subgroupBisSyncState.put(key, value);
-        }
-    }
-
-    private void writeSubgroupMetadataToParcel(
-            @NonNull Parcel dest, @Nullable Map<Integer, byte[]> subgroupMetadata) {
-        if (subgroupMetadata == null) {
-            dest.writeInt(0);
-            return;
-        }
-
-        dest.writeInt(subgroupMetadata.size());
-        for (Map.Entry<Integer, byte[]> entry : subgroupMetadata.entrySet()) {
-            dest.writeInt(entry.getKey());
-            byte[] metadata = entry.getValue();
-            if (metadata != null) {
-                dest.writeInt(metadata.length);
-                dest.writeByteArray(metadata);
-            }
-        }
-    }
-
-    private static void readSubgroupMetadataFromParcel(
-            @NonNull Parcel in, @NonNull Map<Integer, byte[]> subgroupMetadata) {
-        int size = in.readInt();
-
-        for (int i = 0; i < size; i++) {
-            Integer key = in.readInt();
-            Integer metaDataLen = in.readInt();
-            byte[] metadata = null;
-            if (metaDataLen != 0) {
-                metadata = new byte[metaDataLen];
-                in.readByteArray(metadata);
-            }
-            subgroupMetadata.put(key, metadata);
-        }
-    }
-
-    public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastSourceInfo> CREATOR =
-            new Parcelable.Creator<BluetoothLeBroadcastSourceInfo>() {
-                public @NonNull BluetoothLeBroadcastSourceInfo createFromParcel(
-                        @NonNull Parcel in) {
-                    final byte sourceId = in.readByte();
-                    final int sourceAddressType = in.readInt();
-                    final BluetoothDevice sourceDevice =
-                            in.readTypedObject(BluetoothDevice.CREATOR);
-                    final byte sourceAdvSid = in.readByte();
-                    final int broadcastId = in.readInt();
-                    final int paSyncState = in.readInt();
-                    final int audioSyncState = in.readInt();
-                    final int encryptionStatus = in.readInt();
-                    final int badBroadcastLen = in.readInt();
-                    byte[] badBroadcastCode = null;
-
-                    if (badBroadcastLen > 0) {
-                        badBroadcastCode = new byte[badBroadcastLen];
-                        in.readByteArray(badBroadcastCode);
-                    }
-                    final byte numSubGroups = in.readByte();
-                    final String broadcastCode = in.readString();
-                    Map<Integer, Integer> subgroupBisSyncState = new HashMap<Integer, Integer>();
-                    readSubgroupBisSyncStateFromParcel(in, subgroupBisSyncState);
-                    Map<Integer, byte[]> subgroupMetadata = new HashMap<Integer, byte[]>();
-                    readSubgroupMetadataFromParcel(in, subgroupMetadata);
-
-                    BluetoothLeBroadcastSourceInfo srcInfo =
-                            new BluetoothLeBroadcastSourceInfo(
-                                    sourceId,
-                                    sourceAddressType,
-                                    sourceDevice,
-                                    sourceAdvSid,
-                                    broadcastId,
-                                    paSyncState,
-                                    encryptionStatus,
-                                    audioSyncState,
-                                    badBroadcastCode,
-                                    numSubGroups,
-                                    subgroupBisSyncState,
-                                    subgroupMetadata,
-                                    broadcastCode);
-                    return srcInfo;
-                }
-
-                public @NonNull BluetoothLeBroadcastSourceInfo[] newArray(int size) {
-                    return new BluetoothLeBroadcastSourceInfo[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeByte(mSourceId);
-        out.writeInt(mSourceAddressType);
-        out.writeTypedObject(mSourceDevice, 0);
-        out.writeByte(mSourceAdvSid);
-        out.writeInt(mBroadcastId);
-        out.writeInt(mPaSyncState);
-        out.writeInt(mAudioSyncState);
-        out.writeInt(mEncryptionStatus);
-
-        if (mBadBroadcastCode != null) {
-            out.writeInt(mBadBroadcastCode.length);
-            out.writeByteArray(mBadBroadcastCode);
-        } else {
-            // zero indicates that there is no "bad broadcast code"
-            out.writeInt(0);
-        }
-        out.writeByte(mNumSubGroups);
-        out.writeString(mBroadcastCode);
-        writeSubgroupBisSyncStateToParcel(out, mSubgroupBisSyncState);
-        writeSubgroupMetadataToParcel(out, mSubgroupMetadata);
-    }
-
-    private static void log(@NonNull String msg) {
-        if (DBG) {
-            Log.d(TAG, msg);
-        }
-    }
-}
-;
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
deleted file mode 100644
index fb7789d..0000000
--- a/core/java/android/bluetooth/BluetoothLeCall.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.ParcelUuid;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Representation of Call
- *
- * @hide
- */
-public final class BluetoothLeCall implements Parcelable {
-
-    /** @hide */
-    @IntDef(prefix = "STATE_", value = {
-            STATE_INCOMING,
-            STATE_DIALING,
-            STATE_ALERTING,
-            STATE_ACTIVE,
-            STATE_LOCALLY_HELD,
-            STATE_REMOTELY_HELD,
-            STATE_LOCALLY_AND_REMOTELY_HELD
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface State {
-    }
-
-    /**
-     * A remote party is calling (incoming call).
-     *
-     * @hide
-     */
-    public static final int STATE_INCOMING = 0x00;
-
-    /**
-     * The process to call the remote party has started but the remote party is not
-     * being alerted (outgoing call).
-     *
-     * @hide
-     */
-    public static final int STATE_DIALING = 0x01;
-
-    /**
-     * A remote party is being alerted (outgoing call).
-     *
-     * @hide
-     */
-    public static final int STATE_ALERTING = 0x02;
-
-    /**
-     * The call is in an active conversation.
-     *
-     * @hide
-     */
-    public static final int STATE_ACTIVE = 0x03;
-
-    /**
-     * The call is connected but held locally. “Locally Held” implies that either
-     * the server or the client can affect the state.
-     *
-     * @hide
-     */
-    public static final int STATE_LOCALLY_HELD = 0x04;
-
-    /**
-     * The call is connected but held remotely. “Remotely Held” means that the state
-     * is controlled by the remote party of a call.
-     *
-     * @hide
-     */
-    public static final int STATE_REMOTELY_HELD = 0x05;
-
-    /**
-     * The call is connected but held both locally and remotely.
-     *
-     * @hide
-     */
-    public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
-
-    /**
-     * Whether the call direction is outgoing.
-     *
-     * @hide
-     */
-    public static final int FLAG_OUTGOING_CALL = 0x00000001;
-
-    /**
-     * Whether the call URI and Friendly Name are withheld by server.
-     *
-     * @hide
-     */
-    public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
-
-    /**
-     * Whether the call URI and Friendly Name are withheld by network.
-     *
-     * @hide
-     */
-    public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
-
-    /** Unique UUID that identifies this call */
-    private UUID mUuid;
-
-    /** Remote Caller URI */
-    private String mUri;
-
-    /** Caller friendly name */
-    private String mFriendlyName;
-
-    /** Call state */
-    private @State int mState;
-
-    /** Call flags */
-    private int mCallFlags;
-
-    /** @hide */
-    public BluetoothLeCall(@NonNull BluetoothLeCall that) {
-        mUuid = new UUID(that.getUuid().getMostSignificantBits(),
-                that.getUuid().getLeastSignificantBits());
-        mUri = that.mUri;
-        mFriendlyName = that.mFriendlyName;
-        mState = that.mState;
-        mCallFlags = that.mCallFlags;
-    }
-
-    /** @hide */
-    public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
-            @State int state, int callFlags) {
-        mUuid = uuid;
-        mUri = uri;
-        mFriendlyName = friendlyName;
-        mState = state;
-        mCallFlags = callFlags;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-        BluetoothLeCall that = (BluetoothLeCall) o;
-        return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
-                && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
-                && mCallFlags == that.mCallFlags;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
-    }
-
-    /**
-     * Returns a string representation of this BluetoothLeCall.
-     *
-     * <p>
-     * Currently this is the UUID.
-     *
-     * @return string representation of this BluetoothLeCall
-     */
-    @Override
-    public String toString() {
-        return mUuid.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeParcelable(new ParcelUuid(mUuid), 0);
-        out.writeString(mUri);
-        out.writeString(mFriendlyName);
-        out.writeInt(mState);
-        out.writeInt(mCallFlags);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
-    						    new Parcelable.Creator<BluetoothLeCall>() {
-        public BluetoothLeCall createFromParcel(Parcel in) {
-            return new BluetoothLeCall(in);
-        }
-
-        public BluetoothLeCall[] newArray(int size) {
-            return new BluetoothLeCall[size];
-        }
-    };
-
-    private BluetoothLeCall(Parcel in) {
-        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
-        mUri = in.readString();
-        mFriendlyName = in.readString();
-        mState = in.readInt();
-        mCallFlags = in.readInt();
-    }
-
-    /**
-     * Returns an UUID of this BluetoothLeCall.
-     *
-     * <p>
-     * An UUID is unique identifier of a BluetoothLeCall.
-     *
-     * @return UUID of this BluetoothLeCall
-     * @hide
-     */
-    public @NonNull UUID getUuid() {
-        return mUuid;
-    }
-
-    /**
-     * Returns a URI of the remote party of this BluetoothLeCall.
-     *
-     * @return string representation of this BluetoothLeCall
-     * @hide
-     */
-    public @NonNull String getUri() {
-        return mUri;
-    }
-
-    /**
-     * Returns a friendly name of the call.
-     *
-     * @return friendly name representation of this BluetoothLeCall
-     * @hide
-     */
-    public @NonNull String getFriendlyName() {
-        return mFriendlyName;
-    }
-
-    /**
-     * Returns the call state.
-     *
-     * @return the state of this BluetoothLeCall
-     * @hide
-     */
-    public @State int getState() {
-        return mState;
-    }
-
-    /**
-     * Returns the call flags.
-     *
-     * @return call flags
-     * @hide
-     */
-    public int getCallFlags() {
-        return mCallFlags;
-    }
-
-    /**
-     * Whether the call direction is incoming.
-     *
-     * @return true if incoming call, false otherwise
-     * @hide
-     */
-    public boolean isIncomingCall() {
-        return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
deleted file mode 100644
index fb080c9..0000000
--- a/core/java/android/bluetooth/BluetoothLeCallControl.java
+++ /dev/null
@@ -1,899 +0,0 @@
-/*
- * Copyright 2019 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-import android.annotation.SuppressLint;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-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.UUID;
-import java.util.concurrent.Executor;
-
-/**
- * This class provides the APIs to control the Call Control profile.
- *
- * <p>
- * This class provides Bluetooth Telephone Bearer Service functionality,
- * allowing applications to expose a GATT Service based interface to control the
- * state of the calls by remote devices such as LE audio devices.
- *
- * <p>
- * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothLeCallControl proxy object.
- *
- * @hide
- */
-public final class BluetoothLeCallControl implements BluetoothProfile {
-    private static final String TAG = "BluetoothLeCallControl";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /** @hide */
-    @IntDef(prefix = "RESULT_", value = {
-            RESULT_SUCCESS,
-            RESULT_ERROR_UNKNOWN_CALL_ID,
-            RESULT_ERROR_INVALID_URI,
-            RESULT_ERROR_APPLICATION
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Result {
-    }
-
-    /**
-     * Opcode write was successful.
-     *
-     * @hide
-     */
-    public static final int RESULT_SUCCESS = 0;
-
-    /**
-     * Unknown call Id has been used in the operation.
-     *
-     * @hide
-     */
-    public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
-
-    /**
-     * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
-     *
-     * @hide
-     */
-    public static final int RESULT_ERROR_INVALID_URI = 2;
-
-    /**
-     * Application internal error.
-     *
-     * @hide
-     */
-    public static final int RESULT_ERROR_APPLICATION = 3;
-
-    /** @hide */
-    @IntDef(prefix = "TERMINATION_REASON_", value = {
-            TERMINATION_REASON_INVALID_URI,
-            TERMINATION_REASON_FAIL,
-            TERMINATION_REASON_REMOTE_HANGUP,
-            TERMINATION_REASON_SERVER_HANGUP,
-            TERMINATION_REASON_LINE_BUSY,
-            TERMINATION_REASON_NETWORK_CONGESTION,
-            TERMINATION_REASON_CLIENT_HANGUP,
-            TERMINATION_REASON_NO_SERVICE,
-            TERMINATION_REASON_NO_ANSWER
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TerminationReason {
-    }
-
-    /**
-     * Remote Caller ID value used to place a call was formed improperly.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_INVALID_URI = 0x00;
-
-    /**
-     * Call fail.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_FAIL = 0x01;
-
-    /**
-     * Remote party ended call.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
-
-    /**
-     * Call ended from the server.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
-
-    /**
-     * Line busy.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
-
-    /**
-     * Network congestion.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
-
-    /**
-     * Client terminated.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
-
-    /**
-     * No service.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
-
-    /**
-     * No answer.
-     *
-     * @hide
-     */
-    public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
-
-    /*
-     * Flag indicating support for hold/unhold call feature.
-     *
-     * @hide
-     */
-    public static final int CAPABILITY_HOLD_CALL = 0x00000001;
-
-    /**
-     * Flag indicating support for joining calls feature.
-     *
-     * @hide
-     */
-    public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
-
-    private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
-    private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
-
-    private static final int REG_TIMEOUT = 10000;
-
-    /**
-     * The template class is used to call callback functions on events from the TBS
-     * server. Callback functions are wrapped in this class and registered to the
-     * Android system during app registration.
-     *
-     * @hide
-     */
-    public abstract static class Callback {
-
-        private static final String TAG = "BluetoothLeCallControl.Callback";
-
-        /**
-         * Called when a remote client requested to accept the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to be accepted
-         * @hide
-         */
-        public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
-
-        /**
-         * A remote client has requested to terminate the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to terminate
-         * @hide
-         */
-        public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
-
-        /**
-         * A remote client has requested to hold the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to be put on hold
-         * @hide
-         */
-        public void onHoldCall(int requestId, @NonNull UUID callId) {
-            Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
-        }
-
-        /**
-         * A remote client has requested to unhold the call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The call Id requested to unhold
-         * @hide
-         */
-        public void onUnholdCall(int requestId, @NonNull UUID callId) {
-            Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
-        }
-
-        /**
-         * A remote client has requested to place a call.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callId    The Id to be assigned for the new call
-         * @param uri       The caller URI requested
-         * @hide
-         */
-        public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
-
-        /**
-         * A remote client has requested to join the calls.
-         *
-         * <p>
-         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
-         * request.
-         *
-         * @param requestId The Id of the request
-         * @param callIds   The call Id list requested to join
-         * @hide
-         */
-        public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
-            Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
-        }
-    }
-
-    private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
-
-        private final Executor mExecutor;
-        private final Callback mCallback;
-
-        CallbackWrapper(Executor executor, Callback callback) {
-            mExecutor = executor;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onBearerRegistered(int ccid) {
-            if (mCallback != null) {
-                mCcid = ccid;
-            } else {
-                // registration timeout
-                Log.e(TAG, "onBearerRegistered: mCallback is null");
-            }
-        }
-
-        @Override
-        public void onAcceptCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onTerminateCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onHoldCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onUnholdCall(int requestId, ParcelUuid uuid) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-
-        @Override
-        public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
-            List<UUID> uuids = new ArrayList<>();
-            for (ParcelUuid parcelUuid : parcelUuids) {
-                uuids.add(parcelUuid.getUuid());
-            }
-
-            final long identityToken = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
-            } finally {
-                Binder.restoreCallingIdentity(identityToken);
-            }
-        }
-    };
-
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private volatile IBluetoothLeCallControl mService;
-    private BluetoothAdapter mAdapter;
-    private int mCcid = 0;
-    private String mToken;
-    private Callback mCallback = null;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-        new IBluetoothStateChangeCallback.Stub() {
-        public void onBluetoothStateChange(boolean up) {
-            if (DBG)
-                Log.d(TAG, "onBluetoothStateChange: up=" + up);
-            if (!up) {
-                doUnbind();
-            } else {
-                doBind();
-            }
-        }
-    };
-
-    /**
-     * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
-     * telephone bearer service.
-     */
-    /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
-        mContext = context;
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mServiceListener = listener;
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-
-        doBind();
-    }
-
-    private boolean doBind() {
-        synchronized (mConnection) {
-            if (mService == null) {
-                if (VDBG)
-                    Log.d(TAG, "Binding service...");
-                try {
-                    return mAdapter.getBluetoothManager().
-                            bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
-                            mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to bind TelephoneBearerService", e);
-                }
-            }
-        }
-        return false;
-    }
-
-    private void doUnbind() {
-        synchronized (mConnection) {
-            if (mService != null) {
-                if (VDBG)
-                    Log.d(TAG, "Unbinding service...");
-                try {
-                    mAdapter.getBluetoothManager().
-                        unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
-                        mConnection);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
-                } finally {
-                    mService = null;
-                }
-            }
-        }
-    }
-
-    /* package */ void close() {
-        if (VDBG)
-            log("close()");
-        unregisterBearer();
-
-        IBluetoothManager mgr = mAdapter.getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "", re);
-            }
-        }
-        mServiceListener = null;
-        doUnbind();
-    }
-
-    private IBluetoothLeCallControl getService() {
-        return mService;
-    }
-
-    /**
-     * Not supported
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    public int getConnectionState(@Nullable BluetoothDevice device) {
-        throw new UnsupportedOperationException("not supported");
-    }
-
-    /**
-     * Not supported
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        throw new UnsupportedOperationException("not supported");
-    }
-
-    /**
-     * Not supported
-     *
-     * @throws UnsupportedOperationException
-     */
-    @Override
-    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
-        @NonNull int[] states) {
-        throw new UnsupportedOperationException("not supported");
-    }
-
-    /**
-     * Register Telephone Bearer exposing the interface that allows remote devices
-     * to track and control the call states.
-     *
-     * <p>
-     * This is an asynchronous call. The callback is used to notify success or
-     * failure if the function returns true.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * <!-- The UCI is a String identifier of the telephone bearer as defined at
-     * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
-     * (login required). -->
-     *
-     * <!-- The examples of common URI schemes can be found in
-     * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
-     *
-     * <!-- The Technology is an integer value. The possible values are defined at
-     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
-     * -->
-     *
-     * @param uci          Bearer Unique Client Identifier
-     * @param uriSchemes   URI Schemes supported list
-     * @param capabilities bearer capabilities
-     * @param provider     Network provider name
-     * @param technology   Network technology
-     * @param executor     {@link Executor} object on which callback will be
-     *                     executed. The Executor object is required.
-     * @param callback     {@link Callback} object to which callback messages will
-     *                     be sent. The Callback object is required.
-     * @return true on success, false otherwise
-     * @hide
-     */
-    @SuppressLint("ExecutorRegistration")
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean registerBearer(@Nullable String uci,
-                    @NonNull List<String> uriSchemes, int capabilities,
-                    @NonNull String provider, int technology,
-                    @NonNull Executor executor, @NonNull Callback callback) {
-        if (DBG) {
-            Log.d(TAG, "registerBearer");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("null parameter: " + callback);
-        }
-        if (mCcid != 0) {
-            return false;
-        }
-
-        mToken = uci;
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            if (mCallback != null) {
-                Log.e(TAG, "Bearer can be opened only once");
-                return false;
-            }
-
-            mCallback = callback;
-            try {
-                CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
-                service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
-                                        provider, technology);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-                mCallback = null;
-                return false;
-            }
-
-            if (mCcid == 0) {
-                mCallback = null;
-                return false;
-            }
-
-            return true;
-        }
-
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-
-        return false;
-    }
-
-    /**
-     * Unregister Telephone Bearer Service and destroy all the associated data.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void unregisterBearer() {
-        if (DBG) {
-            Log.d(TAG, "unregisterBearer");
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        int ccid = mCcid;
-        mCcid = 0;
-        mCallback = null;
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.unregisterBearer(mToken);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Get the Content Control ID (CCID) value.
-     *
-     * @return ccid Content Control ID value
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public int getContentControlId() {
-        return mCcid;
-    }
-
-    /**
-     * Notify about the newly added call.
-     *
-     * <p>
-     * This shall be called as early as possible after the call has been added.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param call Newly added call
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onCallAdded(@NonNull BluetoothLeCall call) {
-        if (DBG) {
-            Log.d(TAG, "onCallAdded: call=" + call);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.callAdded(mCcid, call);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Notify about the removed call.
-     *
-     * <p>
-     * This shall be called as early as possible after the call has been removed.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param callId The Id of a call that has been removed
-     * @param reason Call termination reason
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
-        if (DBG) {
-            Log.d(TAG, "callRemoved: callId=" + callId);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.callRemoved(mCcid, new ParcelUuid(callId), reason);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Notify the call state change
-     *
-     * <p>
-     * This shall be called as early as possible after the state of the call has
-     * changed.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param callId The call Id that state has been changed
-     * @param state  Call state
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
-        if (DBG) {
-            Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.callStateChanged(mCcid, new ParcelUuid(callId), state);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Provide the current calls list
-     *
-     * <p>
-     * This function must be invoked after registration if application has any
-     * calls.
-     *
-     * @param calls current calls list
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-     public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.currentCallsList(mCcid, calls);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-    }
-
-    /**
-     * Provide the network current status
-     *
-     * <p>
-     * This function must be invoked on change of network state.
-     *
-     * <p>
-     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * <!-- The Technology is an integer value. The possible values are defined at
-     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
-     * -->
-     *
-     * @param provider   Network provider name
-     * @param technology Network technology
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void networkStateChanged(@NonNull String provider, int technology) {
-        if (DBG) {
-            Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.networkStateChanged(mCcid, provider, technology);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-    }
-
-    /**
-     * Send a response to a call control request to a remote device.
-     *
-     * <p>
-     * This function must be invoked in when a request is received by one of these
-     * callback methods:
-     *
-     * <ul>
-     * <li>{@link Callback#onAcceptCall}
-     * <li>{@link Callback#onTerminateCall}
-     * <li>{@link Callback#onHoldCall}
-     * <li>{@link Callback#onUnholdCall}
-     * <li>{@link Callback#onPlaceCall}
-     * <li>{@link Callback#onJoinCalls}
-     * </ul>
-     *
-     * @param requestId The ID of the request that was received with the callback
-     * @param result    The result of the request to be sent to the remote devices
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void requestResult(int requestId, @Result int result) {
-        if (DBG) {
-            Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
-        }
-        if (mCcid == 0) {
-            return;
-        }
-
-        final IBluetoothLeCallControl service = getService();
-        if (service != null) {
-            try {
-                service.requestResult(mCcid, requestId, result);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-        }
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private final IBluetoothProfileServiceConnection mConnection =
-                                    new IBluetoothProfileServiceConnection.Stub() {
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) {
-                Log.d(TAG, "Proxy object connected");
-            }
-            mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
-            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (DBG) {
-                Log.d(TAG, "Proxy object disconnected");
-            }
-            doUnbind();
-            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
-        }
-    };
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-            case MESSAGE_TBS_SERVICE_CONNECTED: {
-                if (mServiceListener != null) {
-                    mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
-                        BluetoothLeCallControl.this);
-                }
-                break;
-            }
-            case MESSAGE_TBS_SERVICE_DISCONNECTED: {
-                if (mServiceListener != null) {
-                    mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
-                }
-                break;
-            }
-            }
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
deleted file mode 100644
index fef6f22..0000000
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth;
-
-import android.annotation.RequiresFeature;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemService;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * High level manager used to obtain an instance of an {@link BluetoothAdapter}
- * and to conduct overall Bluetooth Management.
- * <p>
- * Use {@link android.content.Context#getSystemService(java.lang.String)}
- * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
- * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
- * </p>
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>
- * For more information about using BLUETOOTH, read the <a href=
- * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer
- * guide.
- * </p>
- * </div>
- *
- * @see Context#getSystemService
- * @see BluetoothAdapter#getDefaultAdapter()
- */
-@SystemService(Context.BLUETOOTH_SERVICE)
-@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
-public final class BluetoothManager {
-    private static final String TAG = "BluetoothManager";
-    private static final boolean DBG = false;
-
-    private final AttributionSource mAttributionSource;
-    private final BluetoothAdapter mAdapter;
-
-    /**
-     * @hide
-     */
-    public BluetoothManager(Context context) {
-        mAttributionSource = (context != null) ? context.getAttributionSource() :
-                AttributionSource.myAttributionSource();
-        mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
-    }
-
-    /**
-     * Get the BLUETOOTH Adapter for this device.
-     *
-     * @return the BLUETOOTH Adapter
-     */
-    @RequiresNoPermission
-    public BluetoothAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Get the current connection state of the profile to the remote device.
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for certain profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of Bluetooth.
-     *
-     * @param device Remote bluetooth device.
-     * @param profile GATT or GATT_SERVER
-     * @return State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED},
-     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
-     * {@link BluetoothProfile#STATE_DISCONNECTING}
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device, int profile) {
-        if (DBG) Log.d(TAG, "getConnectionState()");
-
-        List<BluetoothDevice> connectedDevices = getConnectedDevices(profile);
-        for (BluetoothDevice connectedDevice : connectedDevices) {
-            if (device.equals(connectedDevice)) {
-                return BluetoothProfile.STATE_CONNECTED;
-            }
-        }
-
-        return BluetoothProfile.STATE_DISCONNECTED;
-    }
-
-    /**
-     * Get connected devices for the specified profile.
-     *
-     * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED}
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of Bluetooth for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of Bluetooth.
-     *
-     * @param profile GATT or GATT_SERVER
-     * @return List of devices. The list will be empty on error.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices(int profile) {
-        if (DBG) Log.d(TAG, "getConnectedDevices");
-        return getDevicesMatchingConnectionStates(profile, new int[] {
-                BluetoothProfile.STATE_CONNECTED
-        });
-    }
-
-    /**
-     * Get a list of devices that match any of the given connection
-     * states.
-     *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * @param profile GATT or GATT_SERVER
-     * @param states Array of states. States can be one of {@link BluetoothProfile#STATE_CONNECTED},
-     * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
-     * {@link BluetoothProfile#STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
-        if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates");
-
-        if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
-            throw new IllegalArgumentException("Profile not supported: " + profile);
-        }
-
-        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-
-        try {
-            IBluetoothManager managerService = mAdapter.getBluetoothManager();
-            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
-            if (iGatt == null) return devices;
-            devices = Attributable.setAttributionSource(
-                    iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource),
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-
-        return devices;
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @return BluetoothGattServer instance
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback) {
-
-        return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO));
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @param eatt_support idicates if server should use eatt channel for notifications.
-     * @return BluetoothGattServer instance
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback, boolean eatt_support) {
-        return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support));
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @return BluetoothGattServer instance
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback, int transport) {
-        return (openGattServer(context, callback, transport, false));
-    }
-
-    /**
-     * Open a GATT Server
-     * The callback is used to deliver results to Caller, such as connection status as well
-     * as the results of any other GATT server operations.
-     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
-     * to conduct GATT server operations.
-     *
-     * @param context App context
-     * @param callback GATT server callback handler that will receive asynchronous callbacks.
-     * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
-     * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
-     * BluetoothDevice#TRANSPORT_LE}
-     * @param eatt_support idicates if server should use eatt channel for notifications.
-     * @return BluetoothGattServer instance
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothGattServer openGattServer(Context context,
-            BluetoothGattServerCallback callback, int transport, boolean eatt_support) {
-        if (context == null || callback == null) {
-            throw new IllegalArgumentException("null parameter: " + context + " " + callback);
-        }
-
-        // TODO(Bluetooth) check whether platform support BLE
-        //     Do the check here or in GattServer?
-
-        try {
-            IBluetoothManager managerService = mAdapter.getBluetoothManager();
-            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
-            if (iGatt == null) {
-                Log.e(TAG, "Fail to get GATT Server connection");
-                return null;
-            }
-            BluetoothGattServer mGattServer =
-                    new BluetoothGattServer(iGatt, transport, mAdapter);
-            Boolean regStatus = mGattServer.registerCallback(callback, eatt_support);
-            return regStatus ? mGattServer : null;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return null;
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
deleted file mode 100644
index 56e4972..0000000
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth MAP
- * Profile.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
-
-    private static final String TAG = "BluetoothMap";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /** @hide */
-    @SuppressLint("ActionValue")
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * There was an error trying to obtain the state
-     *
-     * @hide
-     */
-    public static final int STATE_ERROR = -1;
-
-    /** @hide */
-    public static final int RESULT_FAILURE = 0;
-    /** @hide */
-    public static final int RESULT_SUCCESS = 1;
-    /**
-     * Connection canceled before completion.
-     *
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.MAP,
-                    "BluetoothMap", IBluetoothMap.class.getName()) {
-                @Override
-                public IBluetoothMap getServiceInterface(IBinder service) {
-                    return IBluetoothMap.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothMap proxy object.
-     */
-    /* package */ BluetoothMap(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothMap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     *
-     * @hide
-     */
-    @SystemApi
-    public void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothMap getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Get the current state of the BluetoothMap service.
-     *
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
-     * connected to the Map service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getState() {
-        if (VDBG) log("getState()");
-        final IBluetoothMap service = getService();
-        final int defaultValue = BluetoothMap.STATE_ERROR;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getState(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the currently connected remote Bluetooth device (PCE).
-     *
-     * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
-     * this proxy object is not connected to the Map service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getClient() {
-        if (VDBG) log("getClient()");
-        final IBluetoothMap service = getService();
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getClient(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns true if the specified Bluetooth device is connected.
-     * Returns false if not connected, or if this proxy object is not
-     * currently connected to the Map service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected(BluetoothDevice device) {
-        if (VDBG) log("isConnected(" + device + ")");
-        final IBluetoothMap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate connection. Initiation of outgoing connections is not
-     * supported for MAP server.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
-        return false;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothMap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Check class bits for possible Map support.
-     * This is a simple heuristic that tries to guess if a device with the
-     * given class bits might support Map. It is not accurate for all
-     * devices. It tries to err on the side of false positives.
-     *
-     * @return True if this device might support Map.
-     *
-     * @hide
-     */
-    public static boolean doesClassMatchSink(BluetoothClass btClass) {
-        // TODO optimize the rule
-        switch (btClass.getDeviceClass()) {
-            case BluetoothClass.Device.COMPUTER_DESKTOP:
-            case BluetoothClass.Device.COMPUTER_LAPTOP:
-            case BluetoothClass.Device.COMPUTER_SERVER:
-            case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
-        final IBluetoothMap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothMap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothMap service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothMap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                    && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothMap service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
deleted file mode 100644
index 03536f9a..0000000
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth MAP MCE Profile.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothMapClient implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothMapClient";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
-
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
-    /** @hide */
-    @RequiresPermission(android.Manifest.permission.RECEIVE_SMS)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_RECEIVED =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
-    /* Actions to be used for pending intents */
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
-    /** @hide */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
-
-    /**
-     * Action to notify read status changed
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
-
-    /**
-     * Action to notify deleted status changed
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
-            "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
-
-    /**
-     * Extras used in ACTION_MESSAGE_RECEIVED intent.
-     * NOTE: HANDLE is only valid for a single session with the device.
-     */
-    /** @hide */
-    public static final String EXTRA_MESSAGE_HANDLE =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE";
-    /** @hide */
-    public static final String EXTRA_MESSAGE_TIMESTAMP =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP";
-    /** @hide */
-    public static final String EXTRA_MESSAGE_READ_STATUS =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS";
-    /** @hide */
-    public static final String EXTRA_SENDER_CONTACT_URI =
-            "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
-    /** @hide */
-    public static final String EXTRA_SENDER_CONTACT_NAME =
-            "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
-
-    /**
-     * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED
-     * Contains the MAP message deleted status
-     * Possible values are:
-     * true: deleted
-     * false: undeleted
-     *
-     * @hide
-     */
-    public static final String EXTRA_MESSAGE_DELETED_STATUS =
-            "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS";
-
-    /**
-     * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED
-     * Possible values are:
-     * 0: failure
-     * 1: success
-     *
-     * @hide
-     */
-    public static final String EXTRA_RESULT_CODE =
-            "android.bluetooth.device.extra.RESULT_CODE";
-
-    /**
-     * There was an error trying to obtain the state
-     * @hide
-     */
-    public static final int STATE_ERROR = -1;
-
-    /** @hide */
-    public static final int RESULT_FAILURE = 0;
-    /** @hide */
-    public static final int RESULT_SUCCESS = 1;
-    /**
-     * Connection canceled before completion.
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-    /** @hide */
-    private static final int UPLOADING_FEATURE_BITMASK = 0x08;
-
-    /*
-     * UNREAD, READ, UNDELETED, DELETED are passed as parameters
-     * to setMessageStatus to indicate the messages new state.
-     */
-
-    /** @hide */
-    public static final int UNREAD = 0;
-    /** @hide */
-    public static final int READ = 1;
-    /** @hide */
-    public static final int UNDELETED = 2;
-    /** @hide */
-    public static final int DELETED = 3;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
-                    "BluetoothMapClient", IBluetoothMapClient.class.getName()) {
-                @Override
-                public IBluetoothMapClient getServiceInterface(IBinder service) {
-                    return IBluetoothMapClient.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothMapClient proxy object.
-     */
-    /* package */ BluetoothMapClient(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothMap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     * @hide
-     */
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothMapClient getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Returns true if the specified Bluetooth device is connected.
-     * Returns false if not connected, or if this proxy object is not
-     * currently connected to the Map service.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected(BluetoothDevice device) {
-        if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate connection. Initiation of outgoing connections is not
-     * supported for MAP server.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "disconnect(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) Log.d(TAG, "getConnectedDevices()");
-        final IBluetoothMapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
-        final IBluetoothMapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final int defaultValue =  BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Send a message.
-     *
-     * Send an SMS message to either the contacts primary number or the telephone number specified.
-     *
-     * @param device Bluetooth device
-     * @param contacts Uri Collection of the contacts
-     * @param message Message to be sent
-     * @param sentIntent intent issued when message is sent
-     * @param deliveredIntent intent issued when message is delivered
-     * @return true if the message is enqueued, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.SEND_SMS,
-    })
-    public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
-            @NonNull String message, @Nullable PendingIntent sentIntent,
-            @Nullable PendingIntent deliveredIntent) {
-        return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent,
-                deliveredIntent);
-    }
-
-     /**
-     * Send a message.
-     *
-     * Send an SMS message to either the contacts primary number or the telephone number specified.
-     *
-     * @param device Bluetooth device
-     * @param contacts Uri[] of the contacts
-     * @param message Message to be sent
-     * @param sentIntent intent issued when message is sent
-     * @param deliveredIntent intent issued when message is delivered
-     * @return true if the message is enqueued, false on error
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.SEND_SMS,
-    })
-    public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
-            PendingIntent sentIntent, PendingIntent deliveredIntent) {
-        if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
-                        mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get unread messages.  Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}.
-     *
-     * @param device Bluetooth device
-     * @return true if the message is enqueued, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.READ_SMS,
-    })
-    public boolean getUnreadMessages(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.getUnreadMessages(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns the "Uploading" feature bit value from the SDP record's
-     * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114).
-     * @param device The Bluetooth device to get this value for.
-     * @return Returns true if the Uploading bit value in SDP record's
-     *         MapSupportedFeatures field is set. False is returned otherwise.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isUploadingSupported(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")");
-        final IBluetoothMapClient service = getService();
-        final int defaultValue = 0;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getSupportedFeatures(device, mAttributionSource, recv);
-                return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
-                        & UPLOADING_FEATURE_BITMASK) > 0;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Set message status of message on MSE
-     * <p>
-     * When read status changed, the result will be published via
-     * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED}
-     * When deleted status changed, the result will be published via
-     * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED}
-     *
-     * @param device Bluetooth device
-     * @param handle message handle
-     * @param status <code>UNREAD</code> for "unread", <code>READ</code> for
-     *            "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for
-     *            "deleted", otherwise return error
-     * @return <code>true</code> if request has been sent, <code>false</code> on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.READ_SMS,
-    })
-    public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
-        if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
-        final IBluetoothMapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ
-                    || status == UNREAD || status == UNDELETED  || status == DELETED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setMessageStatus(device, handle, status, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java
deleted file mode 100644
index eeaf085..0000000
--- a/core/java/android/bluetooth/BluetoothMasInstance.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public final class BluetoothMasInstance implements Parcelable {
-    private final int mId;
-    private final String mName;
-    private final int mChannel;
-    private final int mMsgTypes;
-
-    public BluetoothMasInstance(int id, String name, int channel, int msgTypes) {
-        mId = id;
-        mName = name;
-        mChannel = channel;
-        mMsgTypes = msgTypes;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (o instanceof BluetoothMasInstance) {
-            return mId == ((BluetoothMasInstance) o).mId;
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return mId + (mChannel << 8) + (mMsgTypes << 16);
-    }
-
-    @Override
-    public String toString() {
-        return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":"
-                + Integer.toHexString(mMsgTypes);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothMasInstance> CREATOR =
-            new Parcelable.Creator<BluetoothMasInstance>() {
-                public BluetoothMasInstance createFromParcel(Parcel in) {
-                    return new BluetoothMasInstance(in.readInt(), in.readString(),
-                            in.readInt(), in.readInt());
-                }
-
-                public BluetoothMasInstance[] newArray(int size) {
-                    return new BluetoothMasInstance[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mId);
-        out.writeString(mName);
-        out.writeInt(mChannel);
-        out.writeInt(mMsgTypes);
-    }
-
-    public static final class MessageType {
-        public static final int EMAIL = 0x01;
-        public static final int SMS_GSM = 0x02;
-        public static final int SMS_CDMA = 0x04;
-        public static final int MMS = 0x08;
-    }
-
-    public int getId() {
-        return mId;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public int getChannel() {
-        return mChannel;
-    }
-
-    public int getMsgTypes() {
-        return mMsgTypes;
-    }
-
-    public boolean msgSupported(int msg) {
-        return (mMsgTypes & msg) != 0;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
deleted file mode 100644
index ac2b3ed..0000000
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.SuppressLint;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * BluetoothOutputStream.
- *
- * Used to read from a Bluetooth socket.
- *
- * @hide
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-/*package*/ final class BluetoothOutputStream extends OutputStream {
-    private BluetoothSocket mSocket;
-
-    /*package*/ BluetoothOutputStream(BluetoothSocket s) {
-        mSocket = s;
-    }
-
-    /**
-     * Close this output stream and the socket associated with it.
-     */
-    public void close() throws IOException {
-        mSocket.close();
-    }
-
-    /**
-     * Writes a single byte to this stream. Only the least significant byte of
-     * the integer {@code oneByte} is written to the stream.
-     *
-     * @param oneByte the byte to be written.
-     * @throws IOException if an error occurs while writing to this stream.
-     * @since Android 1.0
-     */
-    public void write(int oneByte) throws IOException {
-        byte[] b = new byte[1];
-        b[0] = (byte) oneByte;
-        mSocket.write(b, 0, 1);
-    }
-
-    /**
-     * Writes {@code count} bytes from the byte array {@code buffer} starting
-     * at position {@code offset} to this stream.
-     *
-     * @param b the buffer to be written.
-     * @param offset the start position in {@code buffer} from where to get bytes.
-     * @param count the number of bytes from {@code buffer} to write to this stream.
-     * @throws IOException if an error occurs while writing to this stream.
-     * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code count < 0}, or if {@code
-     * offset + count} is bigger than the length of {@code buffer}.
-     * @since Android 1.0
-     */
-    public void write(byte[] b, int offset, int count) throws IOException {
-        if (b == null) {
-            throw new NullPointerException("buffer is null");
-        }
-        if ((offset | count) < 0 || count > b.length - offset) {
-            throw new IndexOutOfBoundsException("invalid offset or length");
-        }
-        mSocket.write(b, offset, count);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
deleted file mode 100644
index d4ad4ef4..0000000
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth Pan
- * Profile.
- *
- * <p>BluetoothPan is a proxy object for controlling the Bluetooth
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothPan proxy object.
- *
- * <p>Each method is protected with its appropriate permission.
- *
- * @hide
- */
-@SystemApi
-public final class BluetoothPan implements BluetoothProfile {
-    private static final String TAG = "BluetoothPan";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Pan
-     * profile.
-     *
-     * <p>This intent will have 4 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is
-     * bound to. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
-     * {@link #LOCAL_PANU_ROLE}
-     */
-    @SuppressLint("ActionValue")
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent
-     * The local role of the PAN profile that the remote device is bound to.
-     * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}.
-     */
-    @SuppressLint("ActionValue")
-    public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
-
-    /**
-     * Intent used to broadcast the change in tethering state of the Pan
-     * Profile
-     *
-     * <p>This intent will have 1 extra:
-     * <ul>
-     * <li> {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth
-     * tethering. </li>
-     * </ul>
-     *
-     * <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or
-     * {@link #TETHERING_STATE_ON}
-     */
-    @RequiresLegacyBluetoothPermission
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_TETHERING_STATE_CHANGED =
-            "android.bluetooth.action.TETHERING_STATE_CHANGED";
-
-    /**
-     * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent
-     * The tethering state of the PAN profile.
-     * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}.
-     */
-    public static final String EXTRA_TETHERING_STATE =
-            "android.bluetooth.extra.TETHERING_STATE";
-
-    /** @hide */
-    @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LocalPanRole {}
-
-    public static final int PAN_ROLE_NONE = 0;
-    /**
-     * The local device is acting as a Network Access Point.
-     */
-    public static final int LOCAL_NAP_ROLE = 1;
-
-    /**
-     * The local device is acting as a PAN User.
-     */
-    public static final int LOCAL_PANU_ROLE = 2;
-
-    /** @hide */
-    @IntDef({PAN_ROLE_NONE, REMOTE_NAP_ROLE, REMOTE_PANU_ROLE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RemotePanRole {}
-
-    public static final int REMOTE_NAP_ROLE = 1;
-
-    public static final int REMOTE_PANU_ROLE = 2;
-
-    /** @hide **/
-    @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TetheringState{}
-
-    public static final int TETHERING_STATE_OFF = 1;
-
-    public static final int TETHERING_STATE_ON = 2;
-    /**
-     * Return codes for the connect and disconnect Bluez / Dbus calls.
-     *
-     * @hide
-     */
-    public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_OPERATION_GENERIC_FAILURE = 1003;
-
-    /**
-     * @hide
-     */
-    public static final int PAN_OPERATION_SUCCESS = 1004;
-
-    private final Context mContext;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.PAN,
-                    "BluetoothPan", IBluetoothPan.class.getName()) {
-                @Override
-                public IBluetoothPan getServiceInterface(IBinder service) {
-                    return IBluetoothPan.Stub.asInterface(service);
-                }
-    };
-
-
-    /**
-     * Create a BluetoothPan proxy object for interacting with the local
-     * Bluetooth Service which handles the Pan profile
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    /* package */ BluetoothPan(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mContext = context;
-        mProfileConnector.connect(context, listener);
-    }
-
-    /**
-     * Closes the connection to the service and unregisters callbacks
-     */
-    @UnsupportedAppUsage
-    void close() {
-        if (VDBG) log("close()");
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothPan getService() {
-        return mProfileConnector.getService();
-    }
-
-    /** @hide */
-    protected void finalize() {
-        close();
-    }
-
-    /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> This API returns false in scenarios like the profile on the
-     * device is already connected or Bluetooth is not turned on.
-     * When this API returns true, it is guaranteed that
-     * connection state intent for the profile will be broadcasted with
-     * the state. Users can get the connection state of the profile
-     * from this intent.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnection from a profile
-     *
-     * <p> This API will return false in scenarios like the profile on the
-     * Bluetooth device is not in connected state etc. When this API returns,
-     * true, it is guaranteed that the connection state change
-     * intent will be broadcasted with the state. Users can get the
-     * disconnection state of the profile from this intent.
-     *
-     * <p> If the disconnection is initiated by a remote device, the state
-     * will transition from {@link #STATE_CONNECTED} to
-     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
-     * host (local) device the state will transition from
-     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
-     * state {@link #STATE_DISCONNECTED}. The transition to
-     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
-     * two scenarios.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on immediate error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (VDBG) log("getConnectedDevices()");
-        final IBluetoothPan service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @Override
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (VDBG) log("getDevicesMatchingStates()");
-        final IBluetoothPan service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getConnectionState(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getState(" + device + ")");
-        final IBluetoothPan service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Turns on/off bluetooth tethering
-     *
-     * @param value is whether to enable or disable bluetooth tethering
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.TETHER_PRIVILEGED,
-    })
-    public void setBluetoothTethering(boolean value) {
-        String pkgName = mContext.getOpPackageName();
-        if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
-        final IBluetoothPan service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setBluetoothTethering(value, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Determines whether tethering is enabled
-     *
-     * @return true if tethering is on, false if not or some error occurred
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isTetheringOn() {
-        if (VDBG) log("isTetheringOn()");
-        final IBluetoothPan service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isTetheringOn(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    @UnsupportedAppUsage
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    @UnsupportedAppUsage
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    @UnsupportedAppUsage
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
deleted file mode 100644
index de2db9c..0000000
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Public API for controlling the Bluetooth Pbap Service. This includes
- * Bluetooth Phone book Access profile.
- * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
- * Service via IPC.
- *
- * Creating a BluetoothPbap object will create a binding with the
- * BluetoothPbap service. Users of this object should call close() when they
- * are finished with the BluetoothPbap, so that this proxy object can unbind
- * from the service.
- *
- * This BluetoothPbap object is not immediately bound to the
- * BluetoothPbap service. Use the ServiceListener interface to obtain a
- * notification when it is bound, this is especially important if you wish to
- * immediately call methods on BluetoothPbap after construction.
- *
- * To get an instance of the BluetoothPbap class, you can call
- * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param
- * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of
- * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}.
- *
- * Android only supports one connected Bluetooth Pce at a time.
- *
- * @hide
- */
-@SystemApi
-public class BluetoothPbap implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothPbap";
-    private static final boolean DBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the PBAP
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}
-     *  can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
-     *  {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
-     *  {@link BluetoothProfile#STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @SuppressLint("ActionValue")
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
-
-    private final AttributionSource mAttributionSource;
-
-    /** @hide */
-    public static final int RESULT_FAILURE = 0;
-    /** @hide */
-    public static final int RESULT_SUCCESS = 1;
-    /**
-     * Connection canceled before completion.
-     *
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-
-    private BluetoothAdapter mAdapter;
-    private final BluetoothProfileConnector<IBluetoothPbap> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.PBAP, "BluetoothPbap",
-                    IBluetoothPbap.class.getName()) {
-                @Override
-                public IBluetoothPbap getServiceInterface(IBinder service) {
-                    return IBluetoothPbap.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothPbap proxy object.
-     *
-     * @hide
-     */
-    public BluetoothPbap(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    /** @hide */
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothPbap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     *
-     * @hide
-     */
-    public synchronized void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothPbap getService() {
-        return (IBluetoothPbap) mProfileConnector.getService();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        log("getConnectedDevices()");
-        final IBluetoothPbap service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return new ArrayList<BluetoothDevice>();
-        }
-        try {
-            return Attributable.setAttributionSource(
-                    service.getConnectedDevices(mAttributionSource), mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return new ArrayList<BluetoothDevice>();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @SystemApi
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
-        log("getConnectionState: device=" + device);
-        try {
-            final IBluetoothPbap service = getService();
-            if (service != null && isEnabled() && isValidDevice(device)) {
-                return service.getConnectionState(device, mAttributionSource);
-            }
-            if (service == null) {
-                Log.w(TAG, "Proxy not attached to service");
-            }
-            return BluetoothProfile.STATE_DISCONNECTED;
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return BluetoothProfile.STATE_DISCONNECTED;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @hide
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
-        final IBluetoothPbap service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return new ArrayList<BluetoothDevice>();
-        }
-        try {
-            return Attributable.setAttributionSource(
-                    service.getDevicesMatchingConnectionStates(states, mAttributionSource),
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return new ArrayList<BluetoothDevice>();
-    }
-
-    /**
-     * Set connection policy of the profile and tries to disconnect it if connectionPolicy is
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of:
-     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
-     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
-     * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        try {
-            final IBluetoothPbap service = getService();
-            if (service != null && isEnabled()
-                    && isValidDevice(device)) {
-                if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                        && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
-                    return false;
-                }
-                return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
-            }
-            if (service == null) Log.w(TAG, "Proxy not attached to service");
-            return false;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-            return false;
-        }
-    }
-
-    /**
-     * Disconnects the current Pbap client (PCE). Currently this call blocks,
-     * it may soon be made asynchronous. Returns false if this proxy object is
-     * not currently connected to the Pbap service.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        log("disconnect()");
-        final IBluetoothPbap service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return false;
-        }
-        try {
-            service.disconnect(device, mAttributionSource);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return false;
-    }
-
-    private boolean isEnabled() {
-        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
-        return false;
-    }
-
-    private boolean isValidDevice(BluetoothDevice device) {
-        if (device == null) return false;
-
-        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
-        return false;
-    }
-
-    private static void log(String msg) {
-        if (DBG) {
-            Log.d(TAG, msg);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
deleted file mode 100644
index e096de8..0000000
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth PBAP Client Profile.
- *
- * @hide
- */
-public final class BluetoothPbapClient implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothPbapClient";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
-
-    /** There was an error trying to obtain the state */
-    public static final int STATE_ERROR = -1;
-
-    public static final int RESULT_FAILURE = 0;
-    public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completion. */
-    public static final int RESULT_CANCELED = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT,
-                    "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
-                @Override
-                public IBluetoothPbapClient getServiceInterface(IBinder service) {
-                    return IBluetoothPbapClient.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothPbapClient proxy object.
-     */
-    BluetoothPbapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) {
-        if (DBG) {
-            Log.d(TAG, "Create BluetoothPbapClient proxy object");
-        }
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothPbapClient will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     */
-    public synchronized void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothPbapClient getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Initiate connection.
-     * Upon successful connection to remote PBAP server the Client will
-     * attempt to automatically download the users phonebook and call log.
-     *
-     * @param device a remote device we want connect to
-     * @return <code>true</code> if command has been issued successfully; <code>false</code>
-     * otherwise;
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) {
-            log("connect(" + device + ") for PBAP Client.");
-        }
-        final IBluetoothPbapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.connect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) {
-            log("disconnect(" + device + ")" + new Exception());
-        }
-        final IBluetoothPbapClient service = getService();
-        final boolean defaultValue = true;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-                return true;
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of connected devices.
-     * Currently at most one.
-     *
-     * @return list of connected devices
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) {
-            log("getConnectedDevices()");
-        }
-        final IBluetoothPbapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) {
-            log("getDevicesMatchingStates()");
-        }
-        final IBluetoothPbapClient service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     */
-    @Override
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) {
-            log("getConnectionState(" + device + ")");
-        }
-        final IBluetoothPbapClient service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) {
-            log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        }
-        final IBluetoothPbapClient service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) {
-            log("getConnectionPolicy(" + device + ")");
-        }
-        final IBluetoothPbapClient service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
deleted file mode 100644
index d0f74e9..0000000
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (C) 2010-2016 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.RequiresNoPermission;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * Public APIs for the Bluetooth Profiles.
- *
- * <p> Clients should call {@link BluetoothAdapter#getProfileProxy},
- * to get the Profile Proxy. Each public profile implements this
- * interface.
- */
-public interface BluetoothProfile {
-
-    /**
-     * Extra for the connection state intents of the individual profiles.
-     *
-     * This extra represents the current connection state of the profile of the
-     * Bluetooth device.
-     */
-    @SuppressLint("ActionValue")
-    String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
-
-    /**
-     * Extra for the connection state intents of the individual profiles.
-     *
-     * This extra represents the previous connection state of the profile of the
-     * Bluetooth device.
-     */
-    @SuppressLint("ActionValue")
-    String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.profile.extra.PREVIOUS_STATE";
-
-    /** The profile is in disconnected state */
-    int STATE_DISCONNECTED = 0;
-    /** The profile is in connecting state */
-    int STATE_CONNECTING = 1;
-    /** The profile is in connected state */
-    int STATE_CONNECTED = 2;
-    /** The profile is in disconnecting state */
-    int STATE_DISCONNECTING = 3;
-
-    /** @hide */
-    @IntDef({
-            STATE_DISCONNECTED,
-            STATE_CONNECTING,
-            STATE_CONNECTED,
-            STATE_DISCONNECTING,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BtProfileState {}
-
-    /**
-     * Headset and Handsfree profile
-     */
-    int HEADSET = 1;
-
-    /**
-     * A2DP profile.
-     */
-    int A2DP = 2;
-
-    /**
-     * Health Profile
-     *
-     * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
-     * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
-     * {@link BluetoothAdapter#listenUsingL2capChannel()}, or
-     * {@link BluetoothDevice#createL2capChannel(int)}
-     */
-    @Deprecated
-    int HEALTH = 3;
-
-    /**
-     * HID Host
-     *
-     * @hide
-     */
-    int HID_HOST = 4;
-
-    /**
-     * PAN Profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int PAN = 5;
-
-    /**
-     * PBAP
-     *
-     * @hide
-     */
-    int PBAP = 6;
-
-    /**
-     * GATT
-     */
-    int GATT = 7;
-
-    /**
-     * GATT_SERVER
-     */
-    int GATT_SERVER = 8;
-
-    /**
-     * MAP Profile
-     *
-     * @hide
-     */
-    int MAP = 9;
-
-    /*
-     * SAP Profile
-     * @hide
-     */
-    int SAP = 10;
-
-    /**
-     * A2DP Sink Profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int A2DP_SINK = 11;
-
-    /**
-     * AVRCP Controller Profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int AVRCP_CONTROLLER = 12;
-
-    /**
-     * AVRCP Target Profile
-     *
-     * @hide
-     */
-    int AVRCP = 13;
-
-    /**
-     * Headset Client - HFP HF Role
-     *
-     * @hide
-     */
-    @SystemApi
-    int HEADSET_CLIENT = 16;
-
-    /**
-     * PBAP Client
-     *
-     * @hide
-     */
-    @SystemApi
-    int PBAP_CLIENT = 17;
-
-    /**
-     * MAP Messaging Client Equipment (MCE)
-     *
-     * @hide
-     */
-    @SystemApi
-    int MAP_CLIENT = 18;
-
-    /**
-     * HID Device
-     */
-    int HID_DEVICE = 19;
-
-    /**
-     * Object Push Profile (OPP)
-     *
-     * @hide
-     */
-    int OPP = 20;
-
-    /**
-     * Hearing Aid Device
-     *
-     */
-    int HEARING_AID = 21;
-
-    /**
-     * LE Audio Device
-     *
-     */
-    int LE_AUDIO = 22;
-
-    /**
-     * Volume Control profile
-     *
-     * @hide
-     */
-    @SystemApi
-    int VOLUME_CONTROL = 23;
-
-    /**
-     * @hide
-     * Media Control Profile server
-     *
-     */
-    int MCP_SERVER = 24;
-
-    /**
-     * Coordinated Set Identification Profile set coordinator
-     *
-     */
-    int CSIP_SET_COORDINATOR = 25;
-
-    /**
-     * LE Audio Broadcast Source
-     *
-     * @hide
-     */
-    int LE_AUDIO_BROADCAST = 26;
-
-    /**
-     * @hide
-     * Telephone Bearer Service from Call Control Profile
-     *
-     */
-    int LE_CALL_CONTROL = 27;
-
-    /**
-     * Max profile ID. This value should be updated whenever a new profile is added to match
-     * the largest value assigned to a profile.
-     *
-     * @hide
-     */
-    int MAX_PROFILE_ID = 27;
-
-    /**
-     * Default priority for devices that we try to auto-connect to and
-     * and allow incoming connections for the profile
-     *
-     * @hide
-     **/
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    int PRIORITY_AUTO_CONNECT = 1000;
-
-    /**
-     * Default priority for devices that allow incoming
-     * and outgoing connections for the profile
-     *
-     * @hide
-     * @deprecated Replaced with {@link #CONNECTION_POLICY_ALLOWED}
-     **/
-    @Deprecated
-    @SystemApi
-    int PRIORITY_ON = 100;
-
-    /**
-     * Default priority for devices that does not allow incoming
-     * connections and outgoing connections for the profile.
-     *
-     * @hide
-     * @deprecated Replaced with {@link #CONNECTION_POLICY_FORBIDDEN}
-     **/
-    @Deprecated
-    @SystemApi
-    int PRIORITY_OFF = 0;
-
-    /**
-     * Default priority when not set or when the device is unpaired
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    int PRIORITY_UNDEFINED = -1;
-
-    /** @hide */
-    @IntDef(prefix = "CONNECTION_POLICY_", value = {CONNECTION_POLICY_ALLOWED,
-            CONNECTION_POLICY_FORBIDDEN, CONNECTION_POLICY_UNKNOWN})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ConnectionPolicy{}
-
-    /**
-     * Default connection policy for devices that allow incoming and outgoing connections
-     * for the profile
-     *
-     * @hide
-     **/
-    @SystemApi
-    int CONNECTION_POLICY_ALLOWED = 100;
-
-    /**
-     * Default connection policy for devices that do not allow incoming or outgoing connections
-     * for the profile.
-     *
-     * @hide
-     **/
-    @SystemApi
-    int CONNECTION_POLICY_FORBIDDEN = 0;
-
-    /**
-     * Default connection policy when not set or when the device is unpaired
-     *
-     * @hide
-     */
-    @SystemApi
-    int CONNECTION_POLICY_UNKNOWN = -1;
-
-    /**
-     * Get connected devices for this specific profile.
-     *
-     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
-     *
-     * @return List of devices. The list will be empty on error.
-     */
-    public List<BluetoothDevice> getConnectedDevices();
-
-    /**
-     * Get a list of devices that match any of the given connection
-     * states.
-     *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
-     */
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
-
-    /**
-     * Get the current connection state of the profile
-     *
-     * @param device Remote bluetooth device.
-     * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
-     * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
-     */
-    @BtProfileState int getConnectionState(BluetoothDevice device);
-
-    /**
-     * An interface for notifying BluetoothProfile IPC clients when they have
-     * been connected or disconnected to the service.
-     */
-    public interface ServiceListener {
-        /**
-         * Called to notify the client when the proxy object has been
-         * connected to the service.
-         *
-         * @param profile - One of {@link #HEADSET} or {@link #A2DP}
-         * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
-         */
-        @RequiresNoPermission
-        public void onServiceConnected(int profile, BluetoothProfile proxy);
-
-        /**
-         * Called to notify the client that this proxy object has been
-         * disconnected from the service.
-         *
-         * @param profile - One of {@link #HEADSET} or {@link #A2DP}
-         */
-        @RequiresNoPermission
-        public void onServiceDisconnected(int profile);
-    }
-
-    /**
-     * Convert an integer value of connection state into human readable string
-     *
-     * @param connectionState - One of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, or {@link #STATE_DISCONNECTED}
-     * @return a string representation of the connection state, STATE_UNKNOWN if the state
-     * is not defined
-     * @hide
-     */
-    static String getConnectionStateName(int connectionState) {
-        switch (connectionState) {
-            case STATE_DISCONNECTED:
-                return "STATE_DISCONNECTED";
-            case STATE_CONNECTING:
-                return "STATE_CONNECTING";
-            case STATE_CONNECTED:
-                return "STATE_CONNECTED";
-            case STATE_DISCONNECTING:
-                return "STATE_DISCONNECTING";
-            default:
-                return "STATE_UNKNOWN";
-        }
-    }
-
-    /**
-     * Convert an integer value of profile ID into human readable string
-     *
-     * @param profile profile ID
-     * @return profile name as String, UNKOWN_PROFILE if the profile ID is not defined.
-     * @hide
-     */
-    static String getProfileName(int profile) {
-        switch(profile) {
-            case HEADSET:
-                return "HEADSET";
-            case A2DP:
-                return "A2DP";
-            case HID_HOST:
-                return "HID_HOST";
-            case PAN:
-                return "PAN";
-            case PBAP:
-                return "PBAP";
-            case GATT:
-                return "GATT";
-            case GATT_SERVER:
-                return "GATT_SERVER";
-            case MAP:
-                return "MAP";
-            case SAP:
-                return "SAP";
-            case A2DP_SINK:
-                return "A2DP_SINK";
-            case AVRCP_CONTROLLER:
-                return "AVRCP_CONTROLLER";
-            case AVRCP:
-                return "AVRCP";
-            case HEADSET_CLIENT:
-                return "HEADSET_CLIENT";
-            case PBAP_CLIENT:
-                return "PBAP_CLIENT";
-            case MAP_CLIENT:
-                return "MAP_CLIENT";
-            case HID_DEVICE:
-                return "HID_DEVICE";
-            case OPP:
-                return "OPP";
-            case HEARING_AID:
-                return "HEARING_AID";
-            case LE_AUDIO:
-                return "LE_AUDIO";
-            default:
-                return "UNKNOWN_PROFILE";
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
deleted file mode 100644
index a457679..0000000
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * 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 android.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import java.util.List;
-/**
- * Connector for Bluetooth profile proxies to bind manager service and
- * profile services
- * @param <T> The Bluetooth profile interface for this connection.
- * @hide
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public abstract class BluetoothProfileConnector<T> {
-    private final CloseGuard mCloseGuard = new CloseGuard();
-    private final int mProfileId;
-    private BluetoothProfile.ServiceListener mServiceListener;
-    private final BluetoothProfile mProfileProxy;
-    private Context mContext;
-    private final String mProfileName;
-    private final String mServiceName;
-    private volatile T mService;
-
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-        public void onBluetoothStateChange(boolean up) {
-            if (up) {
-                doBind();
-            } else {
-                doUnbind();
-            }
-        }
-    };
-
-    private @Nullable ComponentName resolveSystemService(@NonNull Intent intent,
-            @NonNull PackageManager pm) {
-        List<ResolveInfo> results = pm.queryIntentServices(intent,
-                PackageManager.ResolveInfoFlags.of(0));
-        if (results == null) {
-            return null;
-        }
-        ComponentName comp = null;
-        for (int i = 0; i < results.size(); i++) {
-            ResolveInfo ri = results.get(i);
-            if ((ri.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                continue;
-            }
-            ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,
-                    ri.serviceInfo.name);
-            if (comp != null) {
-                throw new IllegalStateException("Multiple system services handle " + intent
-                        + ": " + comp + ", " + foundComp);
-            }
-            comp = foundComp;
-        }
-        return comp;
-    }
-
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            logDebug("Proxy object connected");
-            mService = getServiceInterface(service);
-
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(mProfileId, mProfileProxy);
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-            logDebug("Proxy object disconnected");
-            doUnbind();
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(mProfileId);
-            }
-        }
-    };
-
-    BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
-            String serviceName) {
-        mProfileId = profileId;
-        mProfileProxy = profile;
-        mProfileName = profileName;
-        mServiceName = serviceName;
-    }
-
-    /** {@hide} */
-    @Override
-    public void finalize() {
-        mCloseGuard.warnIfOpen();
-        doUnbind();
-    }
-
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private boolean doBind() {
-        synchronized (mConnection) {
-            if (mService == null) {
-                logDebug("Binding service...");
-                mCloseGuard.open("doUnbind");
-                try {
-                    Intent intent = new Intent(mServiceName);
-                    ComponentName comp = resolveSystemService(intent, mContext.getPackageManager());
-                    intent.setComponent(comp);
-                    if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                            UserHandle.CURRENT)) {
-                        logError("Could not bind to Bluetooth Service with " + intent);
-                        return false;
-                    }
-                } catch (SecurityException se) {
-                    logError("Failed to bind service. " + se);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    private void doUnbind() {
-        synchronized (mConnection) {
-            if (mService != null) {
-                logDebug("Unbinding service...");
-                mCloseGuard.close();
-                try {
-                    mContext.unbindService(mConnection);
-                } catch (IllegalArgumentException ie) {
-                    logError("Unable to unbind service: " + ie);
-                } finally {
-                    mService = null;
-                }
-            }
-        }
-    }
-
-    void connect(Context context, BluetoothProfile.ServiceListener listener) {
-        mContext = context;
-        mServiceListener = listener;
-        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
-
-        // Preserve legacy compatibility where apps were depending on
-        // registerStateChangeCallback() performing a permissions check which
-        // has been relaxed in modern platform versions
-        if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
-                && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Need BLUETOOTH permission");
-        }
-
-        if (mgr != null) {
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                logError("Failed to register state change callback. " + re);
-            }
-        }
-        doBind();
-    }
-
-    void disconnect() {
-        mServiceListener = null;
-        IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
-        if (mgr != null) {
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                logError("Failed to unregister state change callback" + re);
-            }
-        }
-        doUnbind();
-    }
-
-    T getService() {
-        return mService;
-    }
-
-    /**
-     * This abstract function is used to implement method to get the
-     * connected Bluetooth service interface.
-     * @param service the connected binder service.
-     * @return T the binder interface of {@code service}.
-     * @hide
-     */
-    public abstract T getServiceInterface(IBinder service);
-
-    private void logDebug(String log) {
-        Log.d(mProfileName, log);
-    }
-
-    private void logError(String log) {
-        Log.e(mProfileName, log);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
deleted file mode 100644
index 808fa39..0000000
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright (C) 2008 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the APIs to control the Bluetooth SIM
- * Access Profile (SAP).
- *
- * <p>BluetoothSap is a proxy object for controlling the Bluetooth
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothSap proxy object.
- *
- * <p>Each method is protected with its appropriate permission.
- *
- * @hide
- */
-public final class BluetoothSap implements BluetoothProfile {
-
-    private static final String TAG = "BluetoothSap";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Intent used to broadcast the change in connection state of the profile.
-     *
-     * <p>This intent will have 4 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @RequiresLegacyBluetoothPermission
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
-
-    /**
-     * There was an error trying to obtain the state.
-     *
-     * @hide
-     */
-    public static final int STATE_ERROR = -1;
-
-    /**
-     * Connection state change succceeded.
-     *
-     * @hide
-     */
-    public static final int RESULT_SUCCESS = 1;
-
-    /**
-     * Connection canceled before completion.
-     *
-     * @hide
-     */
-    public static final int RESULT_CANCELED = 2;
-
-    private final BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.SAP,
-                    "BluetoothSap", IBluetoothSap.class.getName()) {
-                @Override
-                public IBluetoothSap getServiceInterface(IBinder service) {
-                    return IBluetoothSap.Stub.asInterface(service);
-                }
-    };
-
-    /**
-     * Create a BluetoothSap proxy object.
-     */
-    /* package */ BluetoothSap(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-    }
-
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Close the connection to the backing service.
-     * Other public functions of BluetoothSap will return default error
-     * results once close() has been called. Multiple invocations of close()
-     * are ok.
-     *
-     * @hide
-     */
-    public synchronized void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothSap getService() {
-        return mProfileConnector.getService();
-    }
-
-    /**
-     * Get the current state of the BluetoothSap service.
-     *
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
-     * connected to the Sap service.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public int getState() {
-        if (VDBG) log("getState()");
-        final IBluetoothSap service = getService();
-        final int defaultValue = BluetoothSap.STATE_ERROR;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getState(mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the currently connected remote Bluetooth device (PCE).
-     *
-     * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
-     * this proxy object is not connected to the Sap service.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public BluetoothDevice getClient() {
-        if (VDBG) log("getClient()");
-        final IBluetoothSap service = getService();
-        final BluetoothDevice defaultValue = null;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<BluetoothDevice> recv =
-                        new SynchronousResultReceiver();
-                service.getClient(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Returns true if the specified Bluetooth device is connected.
-     * Returns false if not connected, or if this proxy object is not
-     * currently connected to the Sap service.
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean isConnected(BluetoothDevice device) {
-        if (VDBG) log("isConnected(" + device + ")");
-        final IBluetoothSap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.isConnected(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Initiate connection. Initiation of outgoing connections is not
-     * supported for SAP server.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public boolean connect(BluetoothDevice device) {
-        if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
-        return false;
-    }
-
-    /**
-     * Initiate disconnect.
-     *
-     * @param device Remote Bluetooth Device
-     * @return false on error, true otherwise
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public boolean disconnect(BluetoothDevice device) {
-        if (DBG) log("disconnect(" + device + ")");
-        final IBluetoothSap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.disconnect(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
-        final IBluetoothSap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothSap service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothSap service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Set priority of the profile
-     *
-     * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
-     *
-     * @param device Paired bluetooth device
-     * @param priority
-     * @return true if priority is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothSap service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the priority of the profile.
-     *
-     * <p> The priority can be any of:
-     * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
-     *
-     * @param device Bluetooth device
-     * @return priority of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public int getPriority(BluetoothDevice device) {
-        if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothSap service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.isEnabled();
-    }
-
-    private static boolean isValidDevice(BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
deleted file mode 100644
index bb4e354..0000000
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.SuppressLint;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Handler;
-import android.os.ParcelUuid;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-/**
- * A listening Bluetooth socket.
- *
- * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
- * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
- * side, use a {@link BluetoothServerSocket} to create a listening server
- * socket. When a connection is accepted by the {@link BluetoothServerSocket},
- * it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both initiate
- * an outgoing connection and to manage the connection.
- *
- * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by
- * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It
- * is also known as the Serial Port Profile (SPP). To create a listening
- * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link
- * BluetoothAdapter#listenUsingRfcommWithServiceRecord
- * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}.
- *
- * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a
- * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control.
- * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel
- * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket}
- * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()}
- * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your
- * socket.
- *
- * <p> After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to
- * listen for incoming connection requests. This call will block until a connection is established,
- * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the
- * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link
- * BluetoothServerSocket} when it's no longer needed for accepting
- * connections. Closing the {@link BluetoothServerSocket} will <em>not</em> close the returned
- * {@link BluetoothSocket}.
- *
- * <p>{@link BluetoothServerSocket} is thread
- * safe. In particular, {@link #close} will always immediately abort ongoing
- * operations and close the server socket.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using Bluetooth, read the
- * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
- * </div>
- *
- * {@see BluetoothSocket}
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class BluetoothServerSocket implements Closeable {
-
-    private static final String TAG = "BluetoothServerSocket";
-    private static final boolean DBG = false;
-    @UnsupportedAppUsage(publicAlternatives = "Use public {@link BluetoothServerSocket} API "
-            + "instead.")
-    /*package*/ final BluetoothSocket mSocket;
-    private Handler mHandler;
-    private int mMessage;
-    private int mChannel;
-
-    /**
-     * Construct a socket for incoming connections.
-     *
-     * @param type type of socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param port remote port
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
-            throws IOException {
-        mChannel = port;
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
-        if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            mSocket.setExcludeSdp(true);
-        }
-    }
-
-    /**
-     * Construct a socket for incoming connections.
-     *
-     * @param type type of socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param port remote port
-     * @param mitm enforce person-in-the-middle protection for authentication.
-     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port,
-            boolean mitm, boolean min16DigitPin)
-            throws IOException {
-        mChannel = port;
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm,
-                min16DigitPin);
-        if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            mSocket.setExcludeSdp(true);
-        }
-    }
-
-    /**
-     * Construct a socket for incoming connections.
-     *
-     * @param type type of socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param uuid uuid
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)
-            throws IOException {
-        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid);
-        // TODO: This is the same as mChannel = -1 - is this intentional?
-        mChannel = mSocket.getPort();
-    }
-
-
-    /**
-     * Block until a connection is established.
-     * <p>Returns a connected {@link BluetoothSocket} on successful connection.
-     * <p>Once this call returns, it can be called again to accept subsequent
-     * incoming connections.
-     * <p>{@link #close} can be used to abort this call from another thread.
-     *
-     * @return a connected {@link BluetoothSocket}
-     * @throws IOException on error, for example this call was aborted, or timeout
-     */
-    public BluetoothSocket accept() throws IOException {
-        return accept(-1);
-    }
-
-    /**
-     * Block until a connection is established, with timeout.
-     * <p>Returns a connected {@link BluetoothSocket} on successful connection.
-     * <p>Once this call returns, it can be called again to accept subsequent
-     * incoming connections.
-     * <p>{@link #close} can be used to abort this call from another thread.
-     *
-     * @return a connected {@link BluetoothSocket}
-     * @throws IOException on error, for example this call was aborted, or timeout
-     */
-    public BluetoothSocket accept(int timeout) throws IOException {
-        return mSocket.accept(timeout);
-    }
-
-    /**
-     * Immediately close this socket, and release all associated resources.
-     * <p>Causes blocked calls on this socket in other threads to immediately
-     * throw an IOException.
-     * <p>Closing the {@link BluetoothServerSocket} will <em>not</em>
-     * close any {@link BluetoothSocket} received from {@link #accept()}.
-     */
-    public void close() throws IOException {
-        if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
-        synchronized (this) {
-            if (mHandler != null) {
-                mHandler.obtainMessage(mMessage).sendToTarget();
-            }
-        }
-        mSocket.close();
-    }
-
-    /*package*/
-    synchronized void setCloseHandler(Handler handler, int message) {
-        mHandler = handler;
-        mMessage = message;
-    }
-
-    /*package*/ void setServiceName(String serviceName) {
-        mSocket.setServiceName(serviceName);
-    }
-
-    /**
-     * Returns the channel on which this socket is bound.
-     *
-     * @hide
-     */
-    public int getChannel() {
-        return mChannel;
-    }
-
-    /**
-     * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
-     * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
-     * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link
-     * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this
-     * method is called on non-L2CAP server sockets.
-     *
-     * @return the assigned PSM or LE_PSM value depending on transport
-     */
-    public int getPsm() {
-        return mChannel;
-    }
-
-    /**
-     * Sets the channel on which future sockets are bound.
-     * Currently used only when a channel is auto generated.
-     */
-    /*package*/ void setChannel(int newChannel) {
-        /* TODO: From a design/architecture perspective this is wrong.
-         *       The bind operation should be conducted through this class
-         *       and the resulting port should be kept in mChannel, and
-         *       not set from BluetoothAdapter. */
-        if (mSocket != null) {
-            if (mSocket.getPort() != newChannel) {
-                Log.w(TAG, "The port set is different that the underlying port. mSocket.getPort(): "
-                        + mSocket.getPort() + " requested newChannel: " + newChannel);
-            }
-        }
-        mChannel = newChannel;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("ServerSocket: Type: ");
-        switch (mSocket.getConnectionType()) {
-            case BluetoothSocket.TYPE_RFCOMM: {
-                sb.append("TYPE_RFCOMM");
-                break;
-            }
-            case BluetoothSocket.TYPE_L2CAP: {
-                sb.append("TYPE_L2CAP");
-                break;
-            }
-            case BluetoothSocket.TYPE_L2CAP_LE: {
-                sb.append("TYPE_L2CAP_LE");
-                break;
-            }
-            case BluetoothSocket.TYPE_SCO: {
-                sb.append("TYPE_SCO");
-                break;
-            }
-        }
-        sb.append(" Channel: ").append(mChannel);
-        return sb.toString();
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
deleted file mode 100644
index db5b751..0000000
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ /dev/null
@@ -1,809 +0,0 @@
-/*
- * Copyright (C) 2012 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.bluetooth;
-
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.net.LocalSocket;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.Locale;
-import java.util.UUID;
-
-/**
- * A connected or connecting Bluetooth socket.
- *
- * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
- * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
- * side, use a {@link BluetoothServerSocket} to create a listening server
- * socket. When a connection is accepted by the {@link BluetoothServerSocket},
- * it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both initiate
- * an outgoing connection and to manage the connection.
- *
- * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
- * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
- * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
- *
- * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
- * {@link BluetoothDevice#createRfcommSocketToServiceRecord
- * BluetoothDevice.createRfcommSocketToServiceRecord()}.
- * Then call {@link #connect()} to attempt a connection to the remote device.
- * This call will block until a connection is established or the connection
- * fails.
- *
- * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
- * {@link BluetoothServerSocket} documentation.
- *
- * <p>Once the socket is connected, whether initiated as a client or accepted
- * as a server, open the IO streams by calling {@link #getInputStream} and
- * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
- * and {@link java.io.OutputStream} objects, respectively, which are
- * automatically connected to the socket.
- *
- * <p>{@link BluetoothSocket} is thread
- * safe. In particular, {@link #close} will always immediately abort ongoing
- * operations and close the socket.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using Bluetooth, read the
- * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
- * </div>
- *
- * {@see BluetoothServerSocket}
- * {@see java.io.InputStream}
- * {@see java.io.OutputStream}
- */
-public final class BluetoothSocket implements Closeable {
-    private static final String TAG = "BluetoothSocket";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
-
-    /** @hide */
-    public static final int MAX_RFCOMM_CHANNEL = 30;
-    /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
-
-    /** RFCOMM socket */
-    public static final int TYPE_RFCOMM = 1;
-
-    /** SCO socket */
-    public static final int TYPE_SCO = 2;
-
-    /** L2CAP socket */
-    public static final int TYPE_L2CAP = 3;
-
-    /** L2CAP socket on BR/EDR transport
-     * @hide
-     */
-    public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;
-
-    /** L2CAP socket on LE transport
-     * @hide
-     */
-    public static final int TYPE_L2CAP_LE = 4;
-
-    /*package*/ static final int EBADFD = 77;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    /*package*/ static final int EADDRINUSE = 98;
-
-    /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
-    /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
-    /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2;
-    /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3;
-    /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4;
-
-    private final int mType;  /* one of TYPE_RFCOMM etc */
-    private BluetoothDevice mDevice;    /* remote device */
-    private String mAddress;    /* remote address */
-    private final boolean mAuth;
-    private final boolean mEncrypt;
-    private final BluetoothInputStream mInputStream;
-    private final BluetoothOutputStream mOutputStream;
-    private final ParcelUuid mUuid;
-    /** when true no SPP SDP record will be created */
-    private boolean mExcludeSdp = false;
-    /** when true Person-in-the-middle protection will be enabled */
-    private boolean mAuthMitm = false;
-    /** Minimum 16 digit pin for sec mode 2 connections */
-    private boolean mMin16DigitPin = false;
-    @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.")
-    private ParcelFileDescriptor mPfd;
-    @UnsupportedAppUsage
-    private LocalSocket mSocket;
-    private InputStream mSocketIS;
-    private OutputStream mSocketOS;
-    @UnsupportedAppUsage
-    private int mPort;  /* RFCOMM channel or L2CAP psm */
-    private int mFd;
-    private String mServiceName;
-    private static final int PROXY_CONNECTION_TIMEOUT = 5000;
-
-    private static final int SOCK_SIGNAL_SIZE = 20;
-
-    private ByteBuffer mL2capBuffer = null;
-    private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
-    private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
-
-    private enum SocketState {
-        INIT,
-        CONNECTED,
-        LISTENING,
-        CLOSED,
-    }
-
-    /** prevents all native calls after destroyNative() */
-    private volatile SocketState mSocketState;
-
-    /** protects mSocketState */
-    //private final ReentrantReadWriteLock mLock;
-
-    /**
-     * Construct a BluetoothSocket.
-     *
-     * @param type type of socket
-     * @param fd fd to use for connected socket, or -1 for a new socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param device remote device that this socket can connect to
-     * @param port remote port
-     * @param uuid SDP uuid
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
-            BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
-        this(type, fd, auth, encrypt, device, port, uuid, false, false);
-    }
-
-    /**
-     * Construct a BluetoothSocket.
-     *
-     * @param type type of socket
-     * @param fd fd to use for connected socket, or -1 for a new socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param device remote device that this socket can connect to
-     * @param port remote port
-     * @param uuid SDP uuid
-     * @param mitm enforce person-in-the-middle protection.
-     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
-            BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin)
-            throws IOException {
-        if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
-        if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
-                && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
-                throw new IOException("Invalid RFCOMM channel: " + port);
-            }
-        }
-        if (uuid != null) {
-            mUuid = uuid;
-        } else {
-            mUuid = new ParcelUuid(new UUID(0, 0));
-        }
-        mType = type;
-        mAuth = auth;
-        mAuthMitm = mitm;
-        mMin16DigitPin = min16DigitPin;
-        mEncrypt = encrypt;
-        mDevice = device;
-        mPort = port;
-        mFd = fd;
-
-        mSocketState = SocketState.INIT;
-
-        if (device == null) {
-            // Server socket
-            mAddress = BluetoothAdapter.getDefaultAdapter().getAddress();
-        } else {
-            // Remote socket
-            mAddress = device.getAddress();
-        }
-        mInputStream = new BluetoothInputStream(this);
-        mOutputStream = new BluetoothOutputStream(this);
-    }
-
-    private BluetoothSocket(BluetoothSocket s) {
-        if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
-        mUuid = s.mUuid;
-        mType = s.mType;
-        mAuth = s.mAuth;
-        mEncrypt = s.mEncrypt;
-        mPort = s.mPort;
-        mInputStream = new BluetoothInputStream(this);
-        mOutputStream = new BluetoothOutputStream(this);
-        mMaxRxPacketSize = s.mMaxRxPacketSize;
-        mMaxTxPacketSize = s.mMaxTxPacketSize;
-
-        mServiceName = s.mServiceName;
-        mExcludeSdp = s.mExcludeSdp;
-        mAuthMitm = s.mAuthMitm;
-        mMin16DigitPin = s.mMin16DigitPin;
-    }
-
-    private BluetoothSocket acceptSocket(String remoteAddr) throws IOException {
-        BluetoothSocket as = new BluetoothSocket(this);
-        as.mSocketState = SocketState.CONNECTED;
-        FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
-        if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds));
-        if (fds == null || fds.length != 1) {
-            Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds));
-            as.close();
-            throw new IOException("bt socket acept failed");
-        }
-
-        as.mPfd = ParcelFileDescriptor.dup(fds[0]);
-        as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]);
-        as.mSocketIS = as.mSocket.getInputStream();
-        as.mSocketOS = as.mSocket.getOutputStream();
-        as.mAddress = remoteAddr;
-        as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
-        return as;
-    }
-
-    /**
-     * Construct a BluetoothSocket from address. Used by native code.
-     *
-     * @param type type of socket
-     * @param fd fd to use for connected socket, or -1 for a new socket
-     * @param auth require the remote device to be authenticated
-     * @param encrypt require the connection to be encrypted
-     * @param address remote device that this socket can connect to
-     * @param port remote port
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * privileges
-     */
-    private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
-            int port) throws IOException {
-        this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false);
-    }
-
-    /** @hide */
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private int getSecurityFlags() {
-        int flags = 0;
-        if (mAuth) {
-            flags |= SEC_FLAG_AUTH;
-        }
-        if (mEncrypt) {
-            flags |= SEC_FLAG_ENCRYPT;
-        }
-        if (mExcludeSdp) {
-            flags |= BTSOCK_FLAG_NO_SDP;
-        }
-        if (mAuthMitm) {
-            flags |= SEC_FLAG_AUTH_MITM;
-        }
-        if (mMin16DigitPin) {
-            flags |= SEC_FLAG_AUTH_16_DIGIT;
-        }
-        return flags;
-    }
-
-    /**
-     * Get the remote device this socket is connecting, or connected, to.
-     *
-     * @return remote device
-     */
-    @RequiresNoPermission
-    public BluetoothDevice getRemoteDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Get the input stream associated with this socket.
-     * <p>The input stream will be returned even if the socket is not yet
-     * connected, but operations on that stream will throw IOException until
-     * the associated socket is connected.
-     *
-     * @return InputStream
-     */
-    @RequiresNoPermission
-    public InputStream getInputStream() throws IOException {
-        return mInputStream;
-    }
-
-    /**
-     * Get the output stream associated with this socket.
-     * <p>The output stream will be returned even if the socket is not yet
-     * connected, but operations on that stream will throw IOException until
-     * the associated socket is connected.
-     *
-     * @return OutputStream
-     */
-    @RequiresNoPermission
-    public OutputStream getOutputStream() throws IOException {
-        return mOutputStream;
-    }
-
-    /**
-     * Get the connection status of this socket, ie, whether there is an active connection with
-     * remote device.
-     *
-     * @return true if connected false if not connected
-     */
-    @RequiresNoPermission
-    public boolean isConnected() {
-        return mSocketState == SocketState.CONNECTED;
-    }
-
-    /*package*/ void setServiceName(String name) {
-        mServiceName = name;
-    }
-
-    /**
-     * Attempt to connect to a remote device.
-     * <p>This method will block until a connection is made or the connection
-     * fails. If this method returns without an exception then this socket
-     * is now connected.
-     * <p>Creating new connections to
-     * remote Bluetooth devices should not be attempted while device discovery
-     * is in progress. Device discovery is a heavyweight procedure on the
-     * Bluetooth adapter and will significantly slow a device connection.
-     * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
-     * discovery. Discovery is not managed by the Activity,
-     * but is run as a system service, so an application should always call
-     * {@link BluetoothAdapter#cancelDiscovery()} even if it
-     * did not directly request a discovery, just to be sure.
-     * <p>{@link #close} can be used to abort this call from another thread.
-     *
-     * @throws IOException on error, for example connection failure
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void connect() throws IOException {
-        if (mDevice == null) throw new IOException("Connect is called on null device");
-
-        try {
-            if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
-            IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
-            if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
-            mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
-                    mUuid, mPort, getSecurityFlags());
-            synchronized (this) {
-                if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
-                if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
-                if (mPfd == null) throw new IOException("bt socket connect failed");
-                FileDescriptor fd = mPfd.getFileDescriptor();
-                mSocket = LocalSocket.createConnectedLocalSocket(fd);
-                mSocketIS = mSocket.getInputStream();
-                mSocketOS = mSocket.getOutputStream();
-            }
-            int channel = readInt(mSocketIS);
-            if (channel <= 0) {
-                throw new IOException("bt socket connect failed");
-            }
-            mPort = channel;
-            waitSocketSignal(mSocketIS);
-            synchronized (this) {
-                if (mSocketState == SocketState.CLOSED) {
-                    throw new IOException("bt socket closed");
-                }
-                mSocketState = SocketState.CONNECTED;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            throw new IOException("unable to send RPC: " + e.getMessage());
-        }
-    }
-
-    /**
-     * Currently returns unix errno instead of throwing IOException,
-     * so that BluetoothAdapter can check the error code for EADDRINUSE
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    /*package*/ int bindListen() {
-        int ret;
-        if (mSocketState == SocketState.CLOSED) return EBADFD;
-        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
-        if (bluetoothProxy == null) {
-            Log.e(TAG, "bindListen fail, reason: bluetooth is off");
-            return -1;
-        }
-        try {
-            if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
-            mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
-                    mUuid, mPort, getSecurityFlags());
-        } catch (RemoteException e) {
-            Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            return -1;
-        }
-
-        // read out port number
-        try {
-            synchronized (this) {
-                if (DBG) {
-                    Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
-                }
-                if (mSocketState != SocketState.INIT) return EBADFD;
-                if (mPfd == null) return -1;
-                FileDescriptor fd = mPfd.getFileDescriptor();
-                if (fd == null) {
-                    Log.e(TAG, "bindListen(), null file descriptor");
-                    return -1;
-                }
-
-                if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket");
-                mSocket = LocalSocket.createConnectedLocalSocket(fd);
-                if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()");
-                mSocketIS = mSocket.getInputStream();
-                mSocketOS = mSocket.getOutputStream();
-            }
-            if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
-            int channel = readInt(mSocketIS);
-            synchronized (this) {
-                if (mSocketState == SocketState.INIT) {
-                    mSocketState = SocketState.LISTENING;
-                }
-            }
-            if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
-            if (mPort <= -1) {
-                mPort = channel;
-            } // else ASSERT(mPort == channel)
-            ret = 0;
-        } catch (IOException e) {
-            if (mPfd != null) {
-                try {
-                    mPfd.close();
-                } catch (IOException e1) {
-                    Log.e(TAG, "bindListen, close mPfd: " + e1);
-                }
-                mPfd = null;
-            }
-            Log.e(TAG, "bindListen, fail to get port number, exception: " + e);
-            return -1;
-        }
-        return ret;
-    }
-
-    /*package*/ BluetoothSocket accept(int timeout) throws IOException {
-        BluetoothSocket acceptedSocket;
-        if (mSocketState != SocketState.LISTENING) {
-            throw new IOException("bt socket is not in listen state");
-        }
-        if (timeout > 0) {
-            Log.d(TAG, "accept() set timeout (ms):" + timeout);
-            mSocket.setSoTimeout(timeout);
-        }
-        String RemoteAddr = waitSocketSignal(mSocketIS);
-        if (timeout > 0) {
-            mSocket.setSoTimeout(0);
-        }
-        synchronized (this) {
-            if (mSocketState != SocketState.LISTENING) {
-                throw new IOException("bt socket is not in listen state");
-            }
-            acceptedSocket = acceptSocket(RemoteAddr);
-            //quick drop the reference of the file handle
-        }
-        return acceptedSocket;
-    }
-
-    /*package*/ int available() throws IOException {
-        if (VDBG) Log.d(TAG, "available: " + mSocketIS);
-        return mSocketIS.available();
-    }
-
-    /*package*/ int read(byte[] b, int offset, int length) throws IOException {
-        int ret = 0;
-        if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
-        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
-            int bytesToRead = length;
-            if (VDBG) {
-                Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
-                        + "mL2capBuffer= " + mL2capBuffer);
-            }
-            if (mL2capBuffer == null) {
-                createL2capRxBuffer();
-            }
-            if (mL2capBuffer.remaining() == 0) {
-                if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
-                if (fillL2capRxBuffer() == -1) {
-                    return -1;
-                }
-            }
-            if (bytesToRead > mL2capBuffer.remaining()) {
-                bytesToRead = mL2capBuffer.remaining();
-            }
-            if (VDBG) {
-                Log.v(TAG, "get(): offset: " + offset
-                        + " bytesToRead: " + bytesToRead);
-            }
-            mL2capBuffer.get(b, offset, bytesToRead);
-            ret = bytesToRead;
-        } else {
-            if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
-            ret = mSocketIS.read(b, offset, length);
-        }
-        if (ret < 0) {
-            throw new IOException("bt socket closed, read return: " + ret);
-        }
-        if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
-        return ret;
-    }
-
-    /*package*/ int write(byte[] b, int offset, int length) throws IOException {
-
-        //TODO: Since bindings can exist between the SDU size and the
-        //      protocol, we might need to throw an exception instead of just
-        //      splitting the write into multiple smaller writes.
-        //      Rfcomm uses dynamic allocation, and should not have any bindings
-        //      to the actual message length.
-        if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
-        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
-            if (length <= mMaxTxPacketSize) {
-                mSocketOS.write(b, offset, length);
-            } else {
-                if (DBG) {
-                    Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
-                            + "Packet will be divided into SDU packets of size "
-                            + mMaxTxPacketSize);
-                }
-                int tmpOffset = offset;
-                int bytesToWrite = length;
-                while (bytesToWrite > 0) {
-                    int tmpLength = (bytesToWrite > mMaxTxPacketSize)
-                            ? mMaxTxPacketSize
-                            : bytesToWrite;
-                    mSocketOS.write(b, tmpOffset, tmpLength);
-                    tmpOffset += tmpLength;
-                    bytesToWrite -= tmpLength;
-                }
-            }
-        } else {
-            mSocketOS.write(b, offset, length);
-        }
-        // There is no good way to confirm since the entire process is asynchronous anyway
-        if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
-        return length;
-    }
-
-    @Override
-    public void close() throws IOException {
-        Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS
-                + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket + ", mSocketState: "
-                + mSocketState);
-        if (mSocketState == SocketState.CLOSED) {
-            return;
-        } else {
-            synchronized (this) {
-                if (mSocketState == SocketState.CLOSED) {
-                    return;
-                }
-                mSocketState = SocketState.CLOSED;
-                if (mSocket != null) {
-                    if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
-                    mSocket.shutdownInput();
-                    mSocket.shutdownOutput();
-                    mSocket.close();
-                    mSocket = null;
-                }
-                if (mPfd != null) {
-                    mPfd.close();
-                    mPfd = null;
-                }
-            }
-        }
-    }
-
-    /*package */ void removeChannel() {
-    }
-
-    /*package */ int getPort() {
-        return mPort;
-    }
-
-    /**
-     * Get the maximum supported Transmit packet size for the underlying transport.
-     * Use this to optimize the writes done to the output socket, to avoid sending
-     * half full packets.
-     *
-     * @return the maximum supported Transmit packet size for the underlying transport.
-     */
-    @RequiresNoPermission
-    public int getMaxTransmitPacketSize() {
-        return mMaxTxPacketSize;
-    }
-
-    /**
-     * Get the maximum supported Receive packet size for the underlying transport.
-     * Use this to optimize the reads done on the input stream, as any call to read
-     * will return a maximum of this amount of bytes - or for some transports a
-     * multiple of this value.
-     *
-     * @return the maximum supported Receive packet size for the underlying transport.
-     */
-    @RequiresNoPermission
-    public int getMaxReceivePacketSize() {
-        return mMaxRxPacketSize;
-    }
-
-    /**
-     * Get the type of the underlying connection.
-     *
-     * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
-     */
-    @RequiresNoPermission
-    public int getConnectionType() {
-        if (mType == TYPE_L2CAP_LE) {
-            // Treat the LE CoC to be the same type as L2CAP.
-            return TYPE_L2CAP;
-        }
-        return mType;
-    }
-
-    /**
-     * Change if a SDP entry should be automatically created.
-     * Must be called before calling .bind, for the call to have any effect.
-     *
-     * @param excludeSdp <li>TRUE - do not auto generate SDP record. <li>FALSE - default - auto
-     * generate SPP SDP record.
-     * @hide
-     */
-    @RequiresNoPermission
-    public void setExcludeSdp(boolean excludeSdp) {
-        mExcludeSdp = excludeSdp;
-    }
-
-    /**
-     * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This
-     * parameter is used by the BT Controller to set the maximum transmission packet size on this
-     * connection. This function is currently used for testing only.
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public void requestMaximumTxDataLength() throws IOException {
-        if (mDevice == null) {
-            throw new IOException("requestMaximumTxDataLength is called on null device");
-        }
-
-        try {
-            if (mSocketState == SocketState.CLOSED) {
-                throw new IOException("socket closed");
-            }
-            IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
-            if (bluetoothProxy == null) {
-                throw new IOException("Bluetooth is off");
-            }
-
-            if (DBG) Log.d(TAG, "requestMaximumTxDataLength");
-            bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice);
-        } catch (RemoteException e) {
-            Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            throw new IOException("unable to send RPC: " + e.getMessage());
-        }
-    }
-
-    private String convertAddr(final byte[] addr) {
-        return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
-                addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
-    }
-
-    private String waitSocketSignal(InputStream is) throws IOException {
-        byte[] sig = new byte[SOCK_SIGNAL_SIZE];
-        int ret = readAll(is, sig);
-        if (VDBG) {
-            Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + " bytes signal ret: " + ret);
-        }
-        ByteBuffer bb = ByteBuffer.wrap(sig);
-        /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
-        bb.order(ByteOrder.nativeOrder());
-        int size = bb.getShort();
-        if (size != SOCK_SIGNAL_SIZE) {
-            throw new IOException("Connection failure, wrong signal size: " + size);
-        }
-        byte[] addr = new byte[6];
-        bb.get(addr);
-        int channel = bb.getInt();
-        int status = bb.getInt();
-        mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
-        mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
-        String RemoteAddr = convertAddr(addr);
-        if (VDBG) {
-            Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
-                    + RemoteAddr + ", channel: " + channel + ", status: " + status
-                    + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
-        }
-        if (status != 0) {
-            throw new IOException("Connection failure, status: " + status);
-        }
-        return RemoteAddr;
-    }
-
-    private void createL2capRxBuffer() {
-        if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
-            // Allocate the buffer to use for reads.
-            if (VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
-            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
-            if (VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
-            mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
-            if (VDBG) {
-                Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + mL2capBuffer.remaining());
-            }
-        }
-    }
-
-    private int readAll(InputStream is, byte[] b) throws IOException {
-        int left = b.length;
-        while (left > 0) {
-            int ret = is.read(b, b.length - left, left);
-            if (ret <= 0) {
-                throw new IOException("read failed, socket might closed or timeout, read ret: "
-                        + ret);
-            }
-            left -= ret;
-            if (left != 0) {
-                Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left)
-                        + ", expect size: " + b.length);
-            }
-        }
-        return b.length;
-    }
-
-    private int readInt(InputStream is) throws IOException {
-        byte[] ibytes = new byte[4];
-        int ret = readAll(is, ibytes);
-        if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
-        ByteBuffer bb = ByteBuffer.wrap(ibytes);
-        bb.order(ByteOrder.nativeOrder());
-        return bb.getInt();
-    }
-
-    private int fillL2capRxBuffer() throws IOException {
-        mL2capBuffer.rewind();
-        int ret = mSocketIS.read(mL2capBuffer.array());
-        if (ret == -1) {
-            // reached end of stream - return -1
-            mL2capBuffer.limit(0);
-            return -1;
-        }
-        mL2capBuffer.limit(ret);
-        return ret;
-    }
-
-
-}
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
deleted file mode 100644
index a8ce4b4..0000000
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.SystemApi;
-
-/**
- * A class with constants representing possible return values for Bluetooth APIs. General return
- * values occupy the range 0 to 199. Profile-specific return values occupy the range 200-999.
- * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which
- * occupies the max integer value.
- */
-public final class BluetoothStatusCodes {
-
-    private BluetoothStatusCodes() {}
-
-    /**
-     * Indicates that the API call was successful.
-     */
-    public static final int SUCCESS = 0;
-
-    /**
-     * Error code indicating that Bluetooth is not enabled.
-     */
-    public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1;
-
-    /**
-     * Error code indicating that the API call was initiated by neither the system nor the active
-     * user.
-     */
-    public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;
-
-    /**
-     * Error code indicating that the Bluetooth Device specified is not bonded.
-     */
-    public static final int ERROR_DEVICE_NOT_BONDED = 3;
-
-    /**
-     * Error code indicating that the Bluetooth Device specified is not connected, but is bonded.
-     *
-     * @hide
-     */
-    public static final int ERROR_DEVICE_NOT_CONNECTED = 4;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission.
-     *
-     * @hide
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_ADVERTISE_PERMISSION = 5;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission.
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission.
-     *
-     * @hide
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
-
-    /**
-     * Error code indicating that the caller does not have the
-     * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission.
-     */
-    public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8;
-
-    /**
-     * Error code indicating that the profile service is not bound. You can bind a profile service
-     * by calling {@link BluetoothAdapter#getProfileProxy}.
-     */
-    public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9;
-
-    /**
-     * Indicates that the feature is supported.
-     */
-    public static final int FEATURE_SUPPORTED = 10;
-
-    /**
-     * Indicates that the feature is not supported.
-     */
-    public static final int FEATURE_NOT_SUPPORTED = 11;
-
-    /**
-     * Error code indicating that the device is not the active device for this profile.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_NOT_ACTIVE_DEVICE = 12;
-
-    /**
-     * Error code indicating that there are no active devices for the profile.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_NO_ACTIVE_DEVICES = 13;
-
-    /**
-     * Indicates that the Bluetooth profile is not connected to this device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_PROFILE_NOT_CONNECTED = 14;
-
-    /**
-     * Error code indicating that the requested operation timed out.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_TIMEOUT = 15;
-
-    /**
-     * A GATT writeCharacteristic request is not permitted on the remote device.
-     */
-    public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200;
-
-    /**
-     * A GATT writeCharacteristic request is issued to a busy remote device.
-     */
-    public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201;
-
-    /**
-     * If another application has already requested {@link OobData} then another fetch will be
-     * disallowed until the callback is removed.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000;
-
-    /**
-     * Indicates that the ACL disconnected due to an explicit request from the local device.
-     * <p>
-     * Example cause: This is a normal disconnect reason, e.g., user/app initiates
-     * disconnection.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_LOCAL_REQUEST = 1100;
-
-    /**
-     * Indicates that the ACL disconnected due to an explicit request from the remote device.
-     * <p>
-     * Example cause: This is a normal disconnect reason, e.g., user/app initiates
-     * disconnection.
-     * <p>
-     * Example solution: The app can also prompt the user to check their remote device.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_REMOTE_REQUEST = 1101;
-
-    /**
-     * Generic disconnect reason indicating the ACL disconnected due to an error on the local
-     * device.
-     * <p>
-     * Example solution: Prompt the user to check their local device (e.g., phone, car
-     * headunit).
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_LOCAL = 1102;
-
-    /**
-     * Generic disconnect reason indicating the ACL disconnected due to an error on the remote
-     * device.
-     * <p>
-     * Example solution: Prompt the user to check their remote device (e.g., headset, car
-     * headunit, watch).
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_REMOTE = 1103;
-
-    /**
-     * Indicates that the ACL disconnected due to a timeout.
-     * <p>
-     * Example cause: remote device might be out of range.
-     * <p>
-     * Example solution: Prompt user to verify their remote device is on or in
-     * connection/pairing mode.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_TIMEOUT = 1104;
-
-    /**
-     * Indicates that the ACL disconnected due to link key issues.
-     * <p>
-     * Example cause: Devices are either unpaired or remote device is refusing our pairing
-     * request.
-     * <p>
-     * Example solution: Prompt user to unpair and pair again.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_SECURITY = 1105;
-
-    /**
-     * Indicates that the ACL disconnected due to the local device's system policy.
-     * <p>
-     * Example cause: privacy policy, power management policy, permissions, etc.
-     * <p>
-     * Example solution: Prompt the user to check settings, or check with their system
-     * administrator (e.g. some corp-managed devices do not allow OPP connection).
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_SYSTEM_POLICY = 1106;
-
-    /**
-     * Indicates that the ACL disconnected due to resource constraints, either on the local
-     * device or the remote device.
-     * <p>
-     * Example cause: controller is busy, memory limit reached, maximum number of connections
-     * reached.
-     * <p>
-     * Example solution: The app should wait and try again. If still failing, prompt the user
-     * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED = 1107;
-
-    /**
-     * Indicates that the ACL disconnected because another ACL connection already exists.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS = 1108;
-
-    /**
-     * Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109;
-
-    /**
-     * Indicates that setting the LE Audio Broadcast mode failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED = 1110;
-
-    /**
-     * Indicates that setting a new encryption key for Bluetooth LE Audio Broadcast Source failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED = 1111;
-
-    /**
-     * Indicates that connecting to a remote Broadcast Audio Scan Service failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_CONNECT_FAILED = 1112;
-
-    /**
-     * Indicates that disconnecting from a remote Broadcast Audio Scan Service failed.
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_DISCONNECT_FAILED = 1113;
-
-    /**
-     * Indicates that enabling LE Audio Broadcast encryption failed
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED = 1114;
-
-    /**
-     * Indicates that disabling LE Audio Broadcast encryption failed
-     * <p>
-     * Example solution: Change parameters and try again. If error persists, the app can report
-     * telemetry and/or log the error in a bugreport.
-     *
-     * @hide
-     */
-    public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115;
-
-    /**
-     * Indicates that there is already one device for which SCO audio is connected or connecting.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116;
-
-    /**
-     * Indicates that SCO audio was already not connected for this device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117;
-
-    /**
-     * Indicates that there audio route is currently blocked by the system.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118;
-
-    /**
-     * Indicates that there is an active call preventing this operation from succeeding.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ERROR_CALL_ACTIVE = 1119;
-
-    /**
-     * Indicates that an unknown error has occurred has occurred.
-     */
-    public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;
-}
diff --git a/core/java/android/bluetooth/BluetoothUtils.java b/core/java/android/bluetooth/BluetoothUtils.java
deleted file mode 100644
index 8674692..0000000
--- a/core/java/android/bluetooth/BluetoothUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import java.time.Duration;
-
-/**
- * {@hide}
- */
-public final class BluetoothUtils {
-    /**
-     * This utility class cannot be instantiated
-     */
-    private BluetoothUtils() {}
-
-    /**
-     * Timeout value for synchronous binder call
-     */
-    private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5);
-
-    /**
-     * @return timeout value for synchronous binder call
-     */
-    static Duration getSyncTimeout() {
-        return SYNC_CALLS_TIMEOUT;
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
deleted file mode 100644
index 2a8ff51..0000000
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.ParcelUuid;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.UUID;
-
-/**
- * Static helper methods and constants to decode the ParcelUuid of remote devices.
- *
- * @hide
- */
-@SystemApi
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class BluetoothUuid {
-
-    /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
-     * for the various services.
-     *
-     * The following 128 bit values are calculated as:
-     *  uuid * 2^96 + BASE_UUID
-     */
-
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid A2DP_SINK =
-            ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid A2DP_SOURCE =
-            ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid ADV_AUDIO_DIST =
-            ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HSP =
-            ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HSP_AG =
-            ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HFP =
-            ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HFP_AG =
-            ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid AVRCP_CONTROLLER =
-            ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid AVRCP_TARGET =
-            ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid OBEX_OBJECT_PUSH =
-            ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HID =
-            ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HOGP =
-            ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid PANU =
-            ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid NAP =
-            ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid BNEP =
-            ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid PBAP_PCE =
-            ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid PBAP_PSE =
-            ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MAP =
-            ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MNS =
-            ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MAS =
-            ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid SAP =
-            ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid HEARING_AID =
-            ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid LE_AUDIO =
-            ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid DIP =
-            ParcelUuid.fromString("00001200-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid VOLUME_CONTROL =
-            ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid GENERIC_MEDIA_CONTROL =
-            ParcelUuid.fromString("00001849-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid MEDIA_CONTROL =
-            ParcelUuid.fromString("00001848-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid COORDINATED_SET =
-            ParcelUuid.fromString("00001846-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid CAP =
-            ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB");
-    /** @hide */
-    @NonNull
-    @SystemApi
-    public static final ParcelUuid BASE_UUID =
-            ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
-
-    /**
-     * Length of bytes for 16 bit UUID
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int UUID_BYTES_16_BIT = 2;
-    /**
-     * Length of bytes for 32 bit UUID
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int UUID_BYTES_32_BIT = 4;
-    /**
-     * Length of bytes for 128 bit UUID
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int UUID_BYTES_128_BIT = 16;
-
-    /**
-     * Returns true if there any common ParcelUuids in uuidA and uuidB.
-     *
-     * @param uuidA - List of ParcelUuids
-     * @param uuidB - List of ParcelUuids
-     *
-     * @hide
-     */
-    @SystemApi
-    public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA,
-            @Nullable ParcelUuid[] uuidB) {
-        if (uuidA == null && uuidB == null) return true;
-
-        if (uuidA == null) {
-            return uuidB.length == 0;
-        }
-
-        if (uuidB == null) {
-            return uuidA.length == 0;
-        }
-
-        HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA));
-        for (ParcelUuid uuid : uuidB) {
-            if (uuidSet.contains(uuid)) return true;
-        }
-        return false;
-    }
-
-    /**
-     * Extract the Service Identifier or the actual uuid from the Parcel Uuid.
-     * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid,
-     * this function will return 110B
-     *
-     * @param parcelUuid
-     * @return the service identifier.
-     */
-    private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
-        UUID uuid = parcelUuid.getUuid();
-        long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32;
-        return (int) value;
-    }
-
-    /**
-     * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
-     * but the returned UUID is always in 128-bit format.
-     * Note UUID is little endian in Bluetooth.
-     *
-     * @param uuidBytes Byte representation of uuid.
-     * @return {@link ParcelUuid} parsed from bytes.
-     * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) {
-        if (uuidBytes == null) {
-            throw new IllegalArgumentException("uuidBytes cannot be null");
-        }
-        int length = uuidBytes.length;
-        if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT
-                && length != UUID_BYTES_128_BIT) {
-            throw new IllegalArgumentException("uuidBytes length invalid - " + length);
-        }
-
-        // Construct a 128 bit UUID.
-        if (length == UUID_BYTES_128_BIT) {
-            ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
-            long msb = buf.getLong(8);
-            long lsb = buf.getLong(0);
-            return new ParcelUuid(new UUID(msb, lsb));
-        }
-
-        // For 16 bit and 32 bit UUID we need to convert them to 128 bit value.
-        // 128_bit_value = uuid * 2^96 + BASE_UUID
-        long shortUuid;
-        if (length == UUID_BYTES_16_BIT) {
-            shortUuid = uuidBytes[0] & 0xFF;
-            shortUuid += (uuidBytes[1] & 0xFF) << 8;
-        } else {
-            shortUuid = uuidBytes[0] & 0xFF;
-            shortUuid += (uuidBytes[1] & 0xFF) << 8;
-            shortUuid += (uuidBytes[2] & 0xFF) << 16;
-            shortUuid += (uuidBytes[3] & 0xFF) << 24;
-        }
-        long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);
-        long lsb = BASE_UUID.getUuid().getLeastSignificantBits();
-        return new ParcelUuid(new UUID(msb, lsb));
-    }
-
-    /**
-     * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or
-     * 128-bit UUID, Note returned value is little endian (Bluetooth).
-     *
-     * @param uuid uuid to parse.
-     * @return shortest representation of {@code uuid} as bytes.
-     * @throws IllegalArgumentException If the {@code uuid} is null.
-     *
-     * @hide
-     */
-    public static byte[] uuidToBytes(ParcelUuid uuid) {
-        if (uuid == null) {
-            throw new IllegalArgumentException("uuid cannot be null");
-        }
-
-        if (is16BitUuid(uuid)) {
-            byte[] uuidBytes = new byte[UUID_BYTES_16_BIT];
-            int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
-            uuidBytes[0] = (byte) (uuidVal & 0xFF);
-            uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
-            return uuidBytes;
-        }
-
-        if (is32BitUuid(uuid)) {
-            byte[] uuidBytes = new byte[UUID_BYTES_32_BIT];
-            int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
-            uuidBytes[0] = (byte) (uuidVal & 0xFF);
-            uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
-            uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16);
-            uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24);
-            return uuidBytes;
-        }
-
-        // Construct a 128 bit UUID.
-        long msb = uuid.getUuid().getMostSignificantBits();
-        long lsb = uuid.getUuid().getLeastSignificantBits();
-
-        byte[] uuidBytes = new byte[UUID_BYTES_128_BIT];
-        ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
-        buf.putLong(8, msb);
-        buf.putLong(0, lsb);
-        return uuidBytes;
-    }
-
-    /**
-     * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
-     *
-     * @param parcelUuid
-     * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static boolean is16BitUuid(ParcelUuid parcelUuid) {
-        UUID uuid = parcelUuid.getUuid();
-        if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
-            return false;
-        }
-        return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
-    }
-
-
-    /**
-     * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid.
-     *
-     * @param parcelUuid
-     * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static boolean is32BitUuid(ParcelUuid parcelUuid) {
-        UUID uuid = parcelUuid.getUuid();
-        if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
-            return false;
-        }
-        if (is16BitUuid(parcelUuid)) {
-            return false;
-        }
-        return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
-    }
-
-    private BluetoothUuid() {}
-}
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
deleted file mode 100644
index 27532aa..0000000
--- a/core/java/android/bluetooth/BluetoothVolumeControl.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import static android.bluetooth.BluetoothUtils.getSyncTimeout;
-
-import android.Manifest;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.CloseGuard;
-import android.util.Log;
-
-import com.android.modules.utils.SynchronousResultReceiver;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class provides the public APIs to control the Bluetooth Volume Control service.
- *
- * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothVolumeControl proxy object.
- * @hide
- */
-@SystemApi
-public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable {
-    private static final String TAG = "BluetoothVolumeControl";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private CloseGuard mCloseGuard;
-
-    /**
-     * Intent used to broadcast the change in connection state of the Volume Control
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
-     *
-     * @hide
-     */
-    @SystemApi
-    @SuppressLint("ActionValue")
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
-            "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
-
-    private BluetoothAdapter mAdapter;
-    private final AttributionSource mAttributionSource;
-    private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
-            new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
-                    IBluetoothVolumeControl.class.getName()) {
-                @Override
-                public IBluetoothVolumeControl getServiceInterface(IBinder service) {
-                    return IBluetoothVolumeControl.Stub.asInterface(service);
-                }
-            };
-
-    /**
-     * Create a BluetoothVolumeControl proxy object for interacting with the local
-     * Bluetooth Volume Control service.
-     */
-    /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
-            BluetoothAdapter adapter) {
-        mAdapter = adapter;
-        mAttributionSource = adapter.getAttributionSource();
-        mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
-    }
-
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
-        close();
-    }
-
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void close() {
-        mProfileConnector.disconnect();
-    }
-
-    private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); }
-
-    /**
-     * Get the list of connected devices. Currently at most one.
-     *
-     * @return list of connected devices
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @NonNull List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
-        final IBluetoothVolumeControl service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getConnectedDevices(mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the list of devices matching specified states. Currently at most one.
-     *
-     * @return list of matching devices
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
-        final IBluetoothVolumeControl service = getService();
-        final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver<List<BluetoothDevice>> recv =
-                        new SynchronousResultReceiver();
-                service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
-                return Attributable.setAttributionSource(
-                        recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
-                        mAttributionSource);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get connection state of device
-     *
-     * @return device connection state
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-    public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
-        final IBluetoothVolumeControl service = getService();
-        final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionState(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Tells remote device to set an absolute volume.
-     *
-     * @param volume Absolute volume to be set on remote device.
-     *               Minimum value is 0 and maximum value is 255
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void setVolume(@Nullable BluetoothDevice device,
-            @IntRange(from = 0, to = 255) int volume) {
-        if (DBG) log("setVolume(" + volume + ")");
-        final IBluetoothVolumeControl service = getService();
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled()) {
-            try {
-                final SynchronousResultReceiver recv = new SynchronousResultReceiver();
-                service.setVolume(device, volume, mAttributionSource, recv);
-                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-    }
-
-    /**
-     * Set connection policy of the profile
-     *
-     * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Paired bluetooth device
-     * @param connectionPolicy is the connection policy to set to for this profile
-     * @return true if connectionPolicy is set, false on error
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
-            @ConnectionPolicy int connectionPolicy) {
-        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
-        final IBluetoothVolumeControl service = getService();
-        final boolean defaultValue = false;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)
-                && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
-                    || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
-            try {
-                final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
-                service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Get the connection policy of the profile.
-     *
-     * <p> The connection policy can be any of:
-     * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
-     * {@link #CONNECTION_POLICY_UNKNOWN}
-     *
-     * @param device Bluetooth device
-     * @return connection policy of the device
-     * @hide
-     */
-    @SystemApi
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
-        if (VDBG) log("getConnectionPolicy(" + device + ")");
-        final IBluetoothVolumeControl service = getService();
-        final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (isEnabled() && isValidDevice(device)) {
-            try {
-                final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
-                service.getConnectionPolicy(device, mAttributionSource, recv);
-                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
-            } catch (RemoteException | TimeoutException e) {
-                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        return defaultValue;
-    }
-
-    private boolean isEnabled() {
-        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
-        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
-    }
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/bluetooth/BufferConstraint.java b/core/java/android/bluetooth/BufferConstraint.java
deleted file mode 100644
index cbffc78..0000000
--- a/core/java/android/bluetooth/BufferConstraint.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Stores a codec's constraints on buffering length in milliseconds.
- *
- * {@hide}
- */
-@SystemApi
-public final class BufferConstraint implements Parcelable {
-
-    private static final String TAG = "BufferConstraint";
-    private int mDefaultMillis;
-    private int mMaxMillis;
-    private int mMinMillis;
-
-    public BufferConstraint(int defaultMillis, int maxMillis,
-            int minMillis) {
-        mDefaultMillis = defaultMillis;
-        mMaxMillis = maxMillis;
-        mMinMillis = minMillis;
-    }
-
-    BufferConstraint(Parcel in) {
-        mDefaultMillis = in.readInt();
-        mMaxMillis = in.readInt();
-        mMinMillis = in.readInt();
-    }
-
-    public static final @NonNull Parcelable.Creator<BufferConstraint> CREATOR =
-            new Parcelable.Creator<BufferConstraint>() {
-                public BufferConstraint createFromParcel(Parcel in) {
-                    return new BufferConstraint(in);
-                }
-
-                public BufferConstraint[] newArray(int size) {
-                    return new BufferConstraint[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeInt(mDefaultMillis);
-        out.writeInt(mMaxMillis);
-        out.writeInt(mMinMillis);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Get the default buffer millis
-     *
-     * @return default buffer millis
-     * @hide
-     */
-    @SystemApi
-    public int getDefaultMillis() {
-        return mDefaultMillis;
-    }
-
-    /**
-     * Get the maximum buffer millis
-     *
-     * @return maximum buffer millis
-     * @hide
-     */
-    @SystemApi
-    public int getMaxMillis() {
-        return mMaxMillis;
-    }
-
-    /**
-     * Get the minimum buffer millis
-     *
-     * @return minimum buffer millis
-     * @hide
-     */
-    @SystemApi
-    public int getMinMillis() {
-        return mMinMillis;
-    }
-}
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
deleted file mode 100644
index 97d9723..0000000
--- a/core/java/android/bluetooth/BufferConstraints.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.bluetooth;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * A parcelable collection of buffer constraints by codec type.
- *
- * {@hide}
- */
-@SystemApi
-public final class BufferConstraints implements Parcelable {
-    public static final int BUFFER_CODEC_MAX_NUM = 32;
-
-    private static final String TAG = "BufferConstraints";
-
-    private Map<Integer, BufferConstraint> mBufferConstraints;
-    private List<BufferConstraint> mBufferConstraintList;
-
-    public BufferConstraints(@NonNull List<BufferConstraint>
-            bufferConstraintList) {
-
-        mBufferConstraintList = new ArrayList<BufferConstraint>(bufferConstraintList);
-        mBufferConstraints = new HashMap<Integer, BufferConstraint>();
-        for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) {
-            mBufferConstraints.put(i, bufferConstraintList.get(i));
-        }
-    }
-
-    BufferConstraints(Parcel in) {
-        mBufferConstraintList = new ArrayList<BufferConstraint>();
-        mBufferConstraints = new HashMap<Integer, BufferConstraint>();
-        in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader());
-        for (int i = 0; i < mBufferConstraintList.size(); i++) {
-            mBufferConstraints.put(i, mBufferConstraintList.get(i));
-        }
-    }
-
-    public static final @NonNull Parcelable.Creator<BufferConstraints> CREATOR =
-            new Parcelable.Creator<BufferConstraints>() {
-                public BufferConstraints createFromParcel(Parcel in) {
-                    return new BufferConstraints(in);
-                }
-
-                public BufferConstraints[] newArray(int size) {
-                    return new BufferConstraints[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeList(mBufferConstraintList);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Get the buffer constraints by codec type.
-     *
-     * @param codec Audio codec
-     * @return buffer constraints by codec type.
-     * @hide
-     */
-    @SystemApi
-    public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
-        return mBufferConstraints.get(codec);
-    }
-}
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
deleted file mode 100644
index fbee577..0000000
--- a/core/java/android/bluetooth/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 27441
-
-rahulsabnis@google.com
-sattiraju@google.com
-siyuanh@google.com
-zachoverflow@google.com
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
deleted file mode 100644
index bb0b956..0000000
--- a/core/java/android/bluetooth/OobData.java
+++ /dev/null
@@ -1,958 +0,0 @@
-/**
- * Copyright (C) 2016 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.bluetooth;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Out Of Band Data for Bluetooth device pairing.
- *
- * <p>This object represents optional data obtained from a remote device through
- * an out-of-band channel (eg. NFC, QR).
- *
- * <p>References:
- * NFC AD Forum SSP 1.1 (AD)
- * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf}
- * Core Specification Supplement (CSS) V9
- *
- * <p>There are several BR/EDR Examples
- *
- * <p>Negotiated Handover:
- *   Bluetooth Carrier Configuration Record:
- *    - OOB Data Length
- *    - Device Address
- *    - Class of Device
- *    - Simple Pairing Hash C
- *    - Simple Pairing Randomizer R
- *    - Service Class UUID
- *    - Bluetooth Local Name
- *
- * <p>Static Handover:
- *   Bluetooth Carrier Configuration Record:
- *    - OOB Data Length
- *    - Device Address
- *    - Class of Device
- *    - Service Class UUID
- *    - Bluetooth Local Name
- *
- * <p>Simplified Tag Format for Single BT Carrier:
- *   Bluetooth OOB Data Record:
- *    - OOB Data Length
- *    - Device Address
- *    - Class of Device
- *    - Service Class UUID
- *    - Bluetooth Local Name
- *
- * @hide
- */
-@SystemApi
-public final class OobData implements Parcelable {
-
-    private static final String TAG = "OobData";
-    /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int OOB_LENGTH_OCTETS = 2;
-    /**
-     * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1).
-     * (AD 3.1.2) (CSS 1.6.2)
-     * @hide
-     */
-    @SystemApi
-    public static final int DEVICE_ADDRESS_OCTETS = 7;
-    /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int CLASS_OF_DEVICE_OCTETS = 3;
-    /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int CONFIRMATION_OCTETS = 16;
-    /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
-    @SystemApi
-    public static final int RANDOMIZER_OCTETS = 16;
-    /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_OCTETS = 1;
-    /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
-    @SystemApi
-    public static final int LE_TK_OCTETS = 16;
-    /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
-    @SystemApi
-    public static final int LE_APPEARANCE_OCTETS = 2;
-    /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
-    @SystemApi
-    public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
-
-    // Le Roles
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "LE_DEVICE_ROLE_" },
-        value = {
-            LE_DEVICE_ROLE_PERIPHERAL_ONLY,
-            LE_DEVICE_ROLE_CENTRAL_ONLY,
-            LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
-            LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
-        }
-    )
-    public @interface LeRole {}
-
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
-    /** @hide */
-    @SystemApi
-    public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
-
-    // Le Flags
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-        prefix = { "LE_FLAG_" },
-        value = {
-            LE_FLAG_LIMITED_DISCOVERY_MODE,
-            LE_FLAG_GENERAL_DISCOVERY_MODE,
-            LE_FLAG_BREDR_NOT_SUPPORTED,
-            LE_FLAG_SIMULTANEOUS_CONTROLLER,
-            LE_FLAG_SIMULTANEOUS_HOST
-        }
-    )
-    public @interface LeFlag {}
-
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
-    /** @hide */
-    @SystemApi
-    public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
-
-    /**
-     * Builds an {@link OobData} object and validates that the required combination
-     * of values are present to create the LE specific OobData type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class LeBuilder {
-
-        /**
-         * It is recommended that this Hash C is generated anew for each
-         * pairing.
-         *
-         * <p>It should be noted that on passive NFC this isn't possible as the data is static
-         * and immutable.
-         */
-        private byte[] mConfirmationHash = null;
-
-        /**
-         * Optional, but adds more validity to the pairing.
-         *
-         * <p>If not present a value of 0 is assumed.
-         */
-        private byte[] mRandomizerHash = new byte[] {
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        };
-
-        /**
-         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
-         *
-         * <p>This is the name that may be displayed to the device user as part of the UI.
-         */
-        private byte[] mDeviceName = null;
-
-        /**
-         * Sets the Bluetooth Device name to be used for UI purposes.
-         *
-         * <p>Optional attribute.
-         *
-         * @param deviceName byte array representing the name, may be 0 in length, not null.
-         *
-         * @return {@link OobData#ClassicBuilder}
-         *
-         * @throws NullPointerException if deviceName is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
-            requireNonNull(deviceName);
-            this.mDeviceName = deviceName;
-            return this;
-        }
-
-        /**
-         * The Bluetooth Device Address is the address to which the OOB data belongs.
-         *
-         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
-         *
-         * <p> Address is encoded in Little Endian order.
-         *
-         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
-         */
-        private final byte[] mDeviceAddressWithType;
-
-        /**
-         * During an LE connection establishment, one must be in the Peripheral mode and the other
-         * in the Central role.
-         *
-         * <p>Possible Values:
-         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
-         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
-         * Peripheral Preferred
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
-         * 0x04 - 0xFF Reserved
-         */
-        private final @LeRole int mLeDeviceRole;
-
-        /**
-         * Temporary key value from the Security Manager.
-         *
-         * <p> Must be {@link LE_TK_OCTETS} in size
-         */
-        private byte[] mLeTemporaryKey = null;
-
-        /**
-         * Defines the representation of the external appearance of the device.
-         *
-         * <p>For example, a mouse, remote control, or keyboard.
-         *
-         * <p>Used for visual on discovering device to represent icon/string/etc...
-         */
-        private byte[] mLeAppearance = null;
-
-        /**
-         * Contains which discoverable mode to use, BR/EDR support and capability.
-         *
-         * <p>Possible LE Flags:
-         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
-         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
-         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
-         * LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Controller).
-         * Bit 49 of LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Host).
-         * Bit 55 of LMP Feature Mask Definitions.
-         * <b>0x05- 0x07 Reserved</b>
-         */
-        private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
-
-        /**
-         * Main creation method for creating a LE version of {@link OobData}.
-         *
-         * <p>This object will allow the caller to call {@link LeBuilder#build()}
-         * to build the data object or add any option information to the builder.
-         *
-         * @param deviceAddressWithType the LE device address plus the address type (7 octets);
-         * not null.
-         * @param leDeviceRole whether the device supports Peripheral, Central,
-         * Both including preference; not null. (1 octet)
-         * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
-         * of data. Data is derived from controller/host stack and is
-         * required for pairing OOB.
-         *
-         * <p>Possible Values:
-         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
-         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
-         * Peripheral Preferred
-         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
-         * 0x04 - 0xFF Reserved
-         *
-         * @throws IllegalArgumentException if any of the values fail to be set.
-         * @throws NullPointerException if any argument is null.
-         *
-         * @hide
-         */
-        @SystemApi
-        public LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType,
-                @LeRole int leDeviceRole) {
-            requireNonNull(confirmationHash);
-            requireNonNull(deviceAddressWithType);
-            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
-                throw new IllegalArgumentException("confirmationHash must be "
-                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
-            }
-            this.mConfirmationHash = confirmationHash;
-            if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
-                throw new IllegalArgumentException("confirmationHash must be "
-                    + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length.");
-            }
-            this.mDeviceAddressWithType = deviceAddressWithType;
-            if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
-                    || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
-                throw new IllegalArgumentException("leDeviceRole must be a valid value.");
-            }
-            this.mLeDeviceRole = leDeviceRole;
-        }
-
-        /**
-         * Sets the Temporary Key value to be used by the LE Security Manager during
-         * LE pairing.
-         *
-         * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6,
-         * Part A 1.8 for a detailed description.
-         *
-         * @return {@link OobData#Builder}
-         *
-         * @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
-         * @throws NullinterException if leTemporaryKey is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
-            requireNonNull(leTemporaryKey);
-            if (leTemporaryKey.length != LE_TK_OCTETS) {
-                throw new IllegalArgumentException("leTemporaryKey must be "
-                        + LE_TK_OCTETS + " octets in length.");
-            }
-            this.mLeTemporaryKey = leTemporaryKey;
-            return this;
-        }
-
-        /**
-         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
-         * of data. Data is derived from controller/host stack and is required for pairing OOB.
-         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
-         *
-         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
-         * @throws NullPointerException if randomizerHash is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
-            requireNonNull(randomizerHash);
-            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
-                throw new IllegalArgumentException("randomizerHash must be "
-                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
-            }
-            this.mRandomizerHash = randomizerHash;
-            return this;
-        }
-
-        /**
-         * Sets the LE Flags necessary for the pairing scenario or discovery mode.
-         *
-         * @param leFlags enum value representing the 1 octet of data about discovery modes.
-         *
-         * <p>Possible LE Flags:
-         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
-         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
-         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
-         * LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions.
-         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
-         * Same Device Capable (Host).
-         * Bit 55 of LMP Feature Mask Definitions.
-         * 0x05- 0x07 Reserved
-         *
-         * @throws IllegalArgumentException for invalid flag
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public LeBuilder setLeFlags(@LeFlag int leFlags) {
-            if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
-                throw new IllegalArgumentException("leFlags must be a valid value.");
-            }
-            this.mLeFlags = leFlags;
-            return this;
-        }
-
-        /**
-         * Validates and builds the {@link OobData} object for LE Security.
-         *
-         * @return {@link OobData} with given builder values
-         *
-         * @throws IllegalStateException if either of the 2 required fields were not set.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public OobData build() {
-            final OobData oob =
-                    new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole,
-                            this.mConfirmationHash);
-
-            // If we have values, set them, otherwise use default
-            oob.mLeTemporaryKey =
-                    (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
-            oob.mLeAppearance = (this.mLeAppearance != null)
-                    ? this.mLeAppearance : oob.mLeAppearance;
-            oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
-            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
-            oob.mRandomizerHash = this.mRandomizerHash;
-            return oob;
-        }
-    }
-
-    /**
-     * Builds an {@link OobData} object and validates that the required combination
-     * of values are present to create the Classic specific OobData type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class ClassicBuilder {
-        // Used by both Classic and LE
-        /**
-         * It is recommended that this Hash C is generated anew for each
-         * pairing.
-         *
-         * <p>It should be noted that on passive NFC this isn't possible as the data is static
-         * and immutable.
-         *
-         * @hide
-         */
-        private byte[] mConfirmationHash = null;
-
-        /**
-         * Optional, but adds more validity to the pairing.
-         *
-         * <p>If not present a value of 0 is assumed.
-         *
-         * @hide
-         */
-        private byte[] mRandomizerHash = new byte[] {
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        };
-
-        /**
-         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
-         *
-         * <p>This is the name that may be displayed to the device user as part of the UI.
-         *
-         * @hide
-         */
-        private byte[] mDeviceName = null;
-
-        /**
-         * This length value provides the absolute length of total OOB data block used for
-         * Bluetooth BR/EDR
-         *
-         * <p>OOB communication, which includes the length field itself and the Bluetooth
-         * Device Address.
-         *
-         * <p>The minimum length that may be represented in this field is 8.
-         *
-         * @hide
-         */
-        private final byte[] mClassicLength;
-
-        /**
-         * The Bluetooth Device Address is the address to which the OOB data belongs.
-         *
-         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
-         *
-         * <p> Address is encoded in Little Endian order.
-         *
-         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
-         *
-         * @hide
-         */
-        private final byte[] mDeviceAddressWithType;
-
-        /**
-         * Class of Device information is to be used to provide a graphical representation
-         * to the user as part of UI involving operations.
-         *
-         * <p>This is not to be used to determine a particular service can be used.
-         *
-         * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
-         *
-         * @hide
-         */
-        private byte[] mClassOfDevice = null;
-
-        /**
-         * Main creation method for creating a Classic version of {@link OobData}.
-         *
-         * <p>This object will allow the caller to call {@link ClassicBuilder#build()}
-         * to build the data object or add any option information to the builder.
-         *
-         * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
-         * octets of data. Data is derived from controller/host stack and is required for pairing
-         * OOB.
-         * @param classicLength byte array representing the length of data from 8-65535 across 2
-         * octets (0xXXXX).
-         * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
-         * that owns the OOB data. (i.e. the originator) [6 octets]
-         *
-         * @throws IllegalArgumentException if any of the values fail to be set.
-         * @throws NullPointerException if any argument is null.
-         *
-         * @hide
-         */
-        @SystemApi
-        public ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength,
-                @NonNull byte[] deviceAddressWithType) {
-            requireNonNull(confirmationHash);
-            requireNonNull(classicLength);
-            requireNonNull(deviceAddressWithType);
-            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
-                throw new IllegalArgumentException("confirmationHash must be "
-                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
-            }
-            this.mConfirmationHash = confirmationHash;
-            if (classicLength.length != OOB_LENGTH_OCTETS) {
-                throw new IllegalArgumentException("classicLength must be "
-                        + OOB_LENGTH_OCTETS + " octets in length.");
-            }
-            this.mClassicLength = classicLength;
-            if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
-                throw new IllegalArgumentException("deviceAddressWithType must be "
-                        + DEVICE_ADDRESS_OCTETS + " octets in length.");
-            }
-            this.mDeviceAddressWithType = deviceAddressWithType;
-        }
-
-        /**
-         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
-         * of data. Data is derived from controller/host stack and is required for pairing OOB.
-         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
-         *
-         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
-         * @throws NullPointerException if randomizerHash is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
-            requireNonNull(randomizerHash);
-            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
-                throw new IllegalArgumentException("randomizerHash must be "
-                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
-            }
-            this.mRandomizerHash = randomizerHash;
-            return this;
-        }
-
-        /**
-         * Sets the Bluetooth Device name to be used for UI purposes.
-         *
-         * <p>Optional attribute.
-         *
-         * @param deviceName byte array representing the name, may be 0 in length, not null.
-         *
-         * @return {@link OobData#ClassicBuilder}
-         *
-         * @throws NullPointerException if deviceName is null
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
-            requireNonNull(deviceName);
-            this.mDeviceName = deviceName;
-            return this;
-        }
-
-        /**
-         * Sets the Bluetooth Class of Device; used for UI purposes only.
-         *
-         * <p>Not an indicator of available services!
-         *
-         * <p>Optional attribute.
-         *
-         * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
-         *
-         * @return {@link OobData#ClassicBuilder}
-         *
-         * @throws IllegalArgumentException if length is not equal to
-         * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
-         * @throws NullPointerException if classOfDevice is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
-            requireNonNull(classOfDevice);
-            if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
-                throw new IllegalArgumentException("classOfDevice must be "
-                        + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length.");
-            }
-            this.mClassOfDevice = classOfDevice;
-            return this;
-        }
-
-        /**
-         * Validates and builds the {@link OobDat object for Classic Security.
-         *
-         * @return {@link OobData} with previously given builder values.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public OobData build() {
-            final OobData oob =
-                    new OobData(this.mClassicLength, this.mDeviceAddressWithType,
-                            this.mConfirmationHash);
-            // If we have values, set them, otherwise use default
-            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
-            oob.mClassOfDevice = (this.mClassOfDevice != null)
-                    ? this.mClassOfDevice : oob.mClassOfDevice;
-            oob.mRandomizerHash = this.mRandomizerHash;
-            return oob;
-        }
-    }
-
-    // Members (Defaults for Optionals must be set or Parceling fails on NPE)
-    // Both
-    private final byte[] mDeviceAddressWithType;
-    private final byte[] mConfirmationHash;
-    private byte[] mRandomizerHash = new byte[] {
-        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-    };
-    // Default the name to "Bluetooth Device"
-    private byte[] mDeviceName = new byte[] {
-        // Bluetooth
-        0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
-        // <space>Device
-        0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65
-    };
-
-    // Classic
-    private final byte[] mClassicLength;
-    private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
-
-    // LE
-    private final @LeRole int mLeDeviceRole;
-    private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
-    private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
-    private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
-
-    /**
-     * @return byte array representing the MAC address of a bluetooth device.
-     * The Address is 6 octets long with a 1 octet address type associated with the address.
-     *
-     * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type.
-     * For LE there are more choices for Address Type.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getDeviceAddressWithType() {
-        return mDeviceAddressWithType;
-    }
-
-    /**
-     * @return byte array representing the confirmationHash value
-     * which is used to confirm the identity to the controller.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getConfirmationHash() {
-        return mConfirmationHash;
-    }
-
-    /**
-     * @return byte array representing the randomizerHash value
-     * which is used to verify the identity of the controller.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getRandomizerHash() {
-        return mRandomizerHash;
-    }
-
-    /**
-     * @return Device Name used for displaying name in UI.
-     *
-     * <p>Also, this will be populated with the LE Local Name if the data is for LE.
-     *
-     * @hide
-     */
-    @Nullable
-    @SystemApi
-    public byte[] getDeviceName() {
-        return mDeviceName;
-    }
-
-    /**
-     * @return byte array representing the oob data length which is the length
-     * of all of the data including these octets.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getClassicLength() {
-        return mClassicLength;
-    }
-
-    /**
-     * @return byte array representing the class of device for UI display.
-     *
-     * <p>Does not indicate services available; for display only.
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    public byte[] getClassOfDevice() {
-        return mClassOfDevice;
-    }
-
-    /**
-     * @return Temporary Key used for LE pairing.
-     *
-     * @hide
-     */
-    @Nullable
-    @SystemApi
-    public byte[] getLeTemporaryKey() {
-        return mLeTemporaryKey;
-    }
-
-    /**
-     * @return Appearance used for LE pairing. For use in UI situations
-     * when determining what sort of icons or text to display regarding
-     * the device.
-     *
-     * @hide
-     */
-    @Nullable
-    @SystemApi
-    public byte[] getLeAppearance() {
-        return mLeAppearance;
-    }
-
-    /**
-     * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability.
-     *
-     * <p>Possible LE Flags:
-     * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
-     * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
-     * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
-     * LMP Feature Mask Definitions.
-     * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
-     * Same Device Capable (Controller).
-     * Bit 49 of LMP Feature Mask Definitions.
-     * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
-     * Same Device Capable (Host).
-     * Bit 55 of LMP Feature Mask Definitions.
-     * <b>0x05- 0x07 Reserved</b>
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    @LeFlag
-    public int getLeFlags() {
-        return mLeFlags;
-    }
-
-    /**
-     * @return the supported and preferred roles of the LE device.
-     *
-     * <p>Possible Values:
-     * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
-     * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
-     * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
-     * Peripheral Preferred
-     * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
-     * 0x04 - 0xFF Reserved
-     *
-     * @hide
-     */
-    @NonNull
-    @SystemApi
-    @LeRole
-    public int getLeDeviceRole() {
-        return mLeDeviceRole;
-    }
-
-    /**
-     * Classic Security Constructor
-     */
-    private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType,
-            @NonNull byte[] confirmationHash) {
-        mClassicLength = classicLength;
-        mDeviceAddressWithType = deviceAddressWithType;
-        mConfirmationHash = confirmationHash;
-        mLeDeviceRole = -1; // Satisfy final
-    }
-
-    /**
-     * LE Security Constructor
-     */
-    private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole,
-            @NonNull byte[] confirmationHash) {
-        mDeviceAddressWithType = deviceAddressWithType;
-        mLeDeviceRole = leDeviceRole;
-        mConfirmationHash = confirmationHash;
-        mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
-    }
-
-    private OobData(Parcel in) {
-        // Both
-        mDeviceAddressWithType = in.createByteArray();
-        mConfirmationHash = in.createByteArray();
-        mRandomizerHash = in.createByteArray();
-        mDeviceName = in.createByteArray();
-
-        // Classic
-        mClassicLength = in.createByteArray();
-        mClassOfDevice = in.createByteArray();
-
-        // LE
-        mLeDeviceRole = in.readInt();
-        mLeTemporaryKey = in.createByteArray();
-        mLeAppearance = in.createByteArray();
-        mLeFlags = in.readInt();
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        // Both
-        // Required
-        out.writeByteArray(mDeviceAddressWithType);
-        // Required
-        out.writeByteArray(mConfirmationHash);
-        // Optional
-        out.writeByteArray(mRandomizerHash);
-        // Optional
-        out.writeByteArray(mDeviceName);
-
-        // Classic
-        // Required
-        out.writeByteArray(mClassicLength);
-        // Optional
-        out.writeByteArray(mClassOfDevice);
-
-        // LE
-        // Required
-        out.writeInt(mLeDeviceRole);
-        // Required
-        out.writeByteArray(mLeTemporaryKey);
-        // Optional
-        out.writeByteArray(mLeAppearance);
-        // Optional
-        out.writeInt(mLeFlags);
-    }
-
-    // For Parcelable
-    public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
-            new Parcelable.Creator<OobData>() {
-        public OobData createFromParcel(Parcel in) {
-            return new OobData(in);
-        }
-
-        public OobData[] newArray(int size) {
-            return new OobData[size];
-        }
-    };
-
-    /**
-     * @return a {@link String} representation of the OobData object.
-     *
-     * @hide
-     */
-    @Override
-    @NonNull
-    public String toString() {
-        return "OobData: \n\t"
-            // Both
-            + "Device Address With Type: " +  toHexString(mDeviceAddressWithType) + "\n\t"
-            + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t"
-            + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t"
-            + "Device Name: " + toHexString(mDeviceName) + "\n\t"
-            // Classic
-            + "OobData Length: " +  toHexString(mClassicLength) + "\n\t"
-            + "Class of Device: " +  toHexString(mClassOfDevice) + "\n\t"
-            // LE
-            + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t"
-            + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t"
-            + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t"
-            + "LE Flags: " + toHexString(mLeFlags) + "\n\t";
-    }
-
-    @NonNull
-    private String toHexString(int b) {
-        return toHexString(new byte[] {(byte) b});
-    }
-
-    @NonNull
-    private String toHexString(byte b) {
-        return toHexString(new byte[] {b});
-    }
-
-    @NonNull
-    private String toHexString(byte[] array) {
-        if (array == null) return "null";
-        StringBuilder builder = new StringBuilder(array.length * 2);
-        for (byte b: array) {
-            builder.append(String.format("%02x", b));
-        }
-        return builder.toString();
-    }
-}
diff --git a/core/java/android/bluetooth/SdpDipRecord.java b/core/java/android/bluetooth/SdpDipRecord.java
deleted file mode 100644
index 84b0eef..0000000
--- a/core/java/android/bluetooth/SdpDipRecord.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* 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.bluetooth;
-
-import java.util.Arrays;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Data representation of a Object Push Profile Server side SDP record.
- */
-/** @hide */
-public class SdpDipRecord implements Parcelable {
-    private final int mSpecificationId;
-    private final int mVendorId;
-    private final int mVendorIdSource;
-    private final int mProductId;
-    private final int mVersion;
-    private final boolean mPrimaryRecord;
-
-    public SdpDipRecord(int specificationId,
-            int vendorId, int vendorIdSource,
-            int productId, int version,
-            boolean primaryRecord) {
-        super();
-        this.mSpecificationId = specificationId;
-        this.mVendorId = vendorId;
-        this.mVendorIdSource = vendorIdSource;
-        this.mProductId = productId;
-        this.mVersion = version;
-        this.mPrimaryRecord = primaryRecord;
-    }
-
-    public SdpDipRecord(Parcel in) {
-        this.mSpecificationId = in.readInt();
-        this.mVendorId = in.readInt();
-        this.mVendorIdSource = in.readInt();
-        this.mProductId = in.readInt();
-        this.mVersion = in.readInt();
-        this.mPrimaryRecord = in.readBoolean();
-    }
-
-    public int getSpecificationId() {
-        return mSpecificationId;
-    }
-
-    public int getVendorId() {
-        return mVendorId;
-    }
-
-    public int getVendorIdSource() {
-        return mVendorIdSource;
-    }
-
-    public int getProductId() {
-        return mProductId;
-    }
-
-    public int getVersion() {
-        return mVersion;
-    }
-
-    public boolean getPrimaryRecord() {
-        return mPrimaryRecord;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSpecificationId);
-        dest.writeInt(mVendorId);
-        dest.writeInt(mVendorIdSource);
-        dest.writeInt(mProductId);
-        dest.writeInt(mVersion);
-        dest.writeBoolean(mPrimaryRecord);
-    }
-
-    @Override
-    public int describeContents() {
-        /* No special objects */
-        return 0;
-    }
-
-    public static  final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpDipRecord createFromParcel(Parcel in) {
-            return new SdpDipRecord(in);
-        }
-        public SdpDipRecord[] newArray(int size) {
-            return new SdpDipRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpMasRecord.java b/core/java/android/bluetooth/SdpMasRecord.java
deleted file mode 100644
index 72d4938..0000000
--- a/core/java/android/bluetooth/SdpMasRecord.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpMasRecord implements Parcelable {
-    private final int mMasInstanceId;
-    private final int mL2capPsm;
-    private final int mRfcommChannelNumber;
-    private final int mProfileVersion;
-    private final int mSupportedFeatures;
-    private final int mSupportedMessageTypes;
-    private final String mServiceName;
-
-    /** Message type */
-    public static final class MessageType {
-        public static final int EMAIL = 0x01;
-        public static final int SMS_GSM = 0x02;
-        public static final int SMS_CDMA = 0x04;
-        public static final int MMS = 0x08;
-    }
-
-    public SdpMasRecord(int masInstanceId,
-            int l2capPsm,
-            int rfcommChannelNumber,
-            int profileVersion,
-            int supportedFeatures,
-            int supportedMessageTypes,
-            String serviceName) {
-        mMasInstanceId = masInstanceId;
-        mL2capPsm = l2capPsm;
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mProfileVersion = profileVersion;
-        mSupportedFeatures = supportedFeatures;
-        mSupportedMessageTypes = supportedMessageTypes;
-        mServiceName = serviceName;
-    }
-
-    public SdpMasRecord(Parcel in) {
-        mMasInstanceId = in.readInt();
-        mL2capPsm = in.readInt();
-        mRfcommChannelNumber = in.readInt();
-        mProfileVersion = in.readInt();
-        mSupportedFeatures = in.readInt();
-        mSupportedMessageTypes = in.readInt();
-        mServiceName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    public int getMasInstanceId() {
-        return mMasInstanceId;
-    }
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getRfcommCannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public int getSupportedFeatures() {
-        return mSupportedFeatures;
-    }
-
-    public int getSupportedMessageTypes() {
-        return mSupportedMessageTypes;
-    }
-
-    public boolean msgSupported(int msg) {
-        return (mSupportedMessageTypes & msg) != 0;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mMasInstanceId);
-        dest.writeInt(mL2capPsm);
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mProfileVersion);
-        dest.writeInt(mSupportedFeatures);
-        dest.writeInt(mSupportedMessageTypes);
-        dest.writeString(mServiceName);
-    }
-
-    @Override
-    public String toString() {
-        String ret = "Bluetooth MAS SDP Record:\n";
-
-        if (mMasInstanceId != -1) {
-            ret += "Mas Instance Id: " + mMasInstanceId + "\n";
-        }
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mL2capPsm != -1) {
-            ret += "L2CAP PSM: " + mL2capPsm + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "Profile version: " + mProfileVersion + "\n";
-        }
-        if (mSupportedMessageTypes != -1) {
-            ret += "Supported msg types: " + mSupportedMessageTypes + "\n";
-        }
-        if (mSupportedFeatures != -1) {
-            ret += "Supported features: " + mSupportedFeatures + "\n";
-        }
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpMasRecord createFromParcel(Parcel in) {
-            return new SdpMasRecord(in);
-        }
-
-        public SdpRecord[] newArray(int size) {
-            return new SdpRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpMnsRecord.java b/core/java/android/bluetooth/SdpMnsRecord.java
deleted file mode 100644
index a781d5d..0000000
--- a/core/java/android/bluetooth/SdpMnsRecord.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpMnsRecord implements Parcelable {
-    private final int mL2capPsm;
-    private final int mRfcommChannelNumber;
-    private final int mSupportedFeatures;
-    private final int mProfileVersion;
-    private final String mServiceName;
-
-    public SdpMnsRecord(int l2capPsm,
-            int rfcommChannelNumber,
-            int profileVersion,
-            int supportedFeatures,
-            String serviceName) {
-        mL2capPsm = l2capPsm;
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mSupportedFeatures = supportedFeatures;
-        mServiceName = serviceName;
-        mProfileVersion = profileVersion;
-    }
-
-    public SdpMnsRecord(Parcel in) {
-        mRfcommChannelNumber = in.readInt();
-        mL2capPsm = in.readInt();
-        mServiceName = in.readString();
-        mSupportedFeatures = in.readInt();
-        mProfileVersion = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getRfcommChannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getSupportedFeatures() {
-        return mSupportedFeatures;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mL2capPsm);
-        dest.writeString(mServiceName);
-        dest.writeInt(mSupportedFeatures);
-        dest.writeInt(mProfileVersion);
-    }
-
-    public String toString() {
-        String ret = "Bluetooth MNS SDP Record:\n";
-
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mL2capPsm != -1) {
-            ret += "L2CAP PSM: " + mL2capPsm + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mSupportedFeatures != -1) {
-            ret += "Supported features: " + mSupportedFeatures + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "Profile_version: " + mProfileVersion + "\n";
-        }
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpMnsRecord createFromParcel(Parcel in) {
-            return new SdpMnsRecord(in);
-        }
-
-        public SdpMnsRecord[] newArray(int size) {
-            return new SdpMnsRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpOppOpsRecord.java b/core/java/android/bluetooth/SdpOppOpsRecord.java
deleted file mode 100644
index e30745b8..0000000
--- a/core/java/android/bluetooth/SdpOppOpsRecord.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * Data representation of a Object Push Profile Server side SDP record.
- */
-
-/** @hide */
-public class SdpOppOpsRecord implements Parcelable {
-
-    private final String mServiceName;
-    private final int mRfcommChannel;
-    private final int mL2capPsm;
-    private final int mProfileVersion;
-    private final byte[] mFormatsList;
-
-    public SdpOppOpsRecord(String serviceName, int rfcommChannel,
-            int l2capPsm, int version, byte[] formatsList) {
-        super();
-        mServiceName = serviceName;
-        mRfcommChannel = rfcommChannel;
-        mL2capPsm = l2capPsm;
-        mProfileVersion = version;
-        mFormatsList = formatsList;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    public int getRfcommChannel() {
-        return mRfcommChannel;
-    }
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public byte[] getFormatsList() {
-        return mFormatsList;
-    }
-
-    @Override
-    public int describeContents() {
-        /* No special objects */
-        return 0;
-    }
-
-    public SdpOppOpsRecord(Parcel in) {
-        mRfcommChannel = in.readInt();
-        mL2capPsm = in.readInt();
-        mProfileVersion = in.readInt();
-        mServiceName = in.readString();
-        int arrayLength = in.readInt();
-        if (arrayLength > 0) {
-            byte[] bytes = new byte[arrayLength];
-            in.readByteArray(bytes);
-            mFormatsList = bytes;
-        } else {
-            mFormatsList = null;
-        }
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannel);
-        dest.writeInt(mL2capPsm);
-        dest.writeInt(mProfileVersion);
-        dest.writeString(mServiceName);
-        if (mFormatsList != null && mFormatsList.length > 0) {
-            dest.writeInt(mFormatsList.length);
-            dest.writeByteArray(mFormatsList);
-        } else {
-            dest.writeInt(0);
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n");
-        sb.append("  RFCOMM Chan Number: ").append(mRfcommChannel);
-        sb.append("\n  L2CAP PSM: ").append(mL2capPsm);
-        sb.append("\n  Profile version: ").append(mProfileVersion);
-        sb.append("\n  Service Name: ").append(mServiceName);
-        sb.append("\n  Formats List: ").append(Arrays.toString(mFormatsList));
-        return sb.toString();
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpOppOpsRecord createFromParcel(Parcel in) {
-            return new SdpOppOpsRecord(in);
-        }
-
-        public SdpOppOpsRecord[] newArray(int size) {
-            return new SdpOppOpsRecord[size];
-        }
-    };
-
-}
diff --git a/core/java/android/bluetooth/SdpPseRecord.java b/core/java/android/bluetooth/SdpPseRecord.java
deleted file mode 100644
index 72249d0..0000000
--- a/core/java/android/bluetooth/SdpPseRecord.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpPseRecord implements Parcelable {
-    private final int mL2capPsm;
-    private final int mRfcommChannelNumber;
-    private final int mProfileVersion;
-    private final int mSupportedFeatures;
-    private final int mSupportedRepositories;
-    private final String mServiceName;
-
-    public SdpPseRecord(int l2capPsm,
-            int rfcommChannelNumber,
-            int profileVersion,
-            int supportedFeatures,
-            int supportedRepositories,
-            String serviceName) {
-        mL2capPsm = l2capPsm;
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mProfileVersion = profileVersion;
-        mSupportedFeatures = supportedFeatures;
-        mSupportedRepositories = supportedRepositories;
-        mServiceName = serviceName;
-    }
-
-    public SdpPseRecord(Parcel in) {
-        mRfcommChannelNumber = in.readInt();
-        mL2capPsm = in.readInt();
-        mProfileVersion = in.readInt();
-        mSupportedFeatures = in.readInt();
-        mSupportedRepositories = in.readInt();
-        mServiceName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    public int getL2capPsm() {
-        return mL2capPsm;
-    }
-
-    public int getRfcommChannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getSupportedFeatures() {
-        return mSupportedFeatures;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public int getSupportedRepositories() {
-        return mSupportedRepositories;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mL2capPsm);
-        dest.writeInt(mProfileVersion);
-        dest.writeInt(mSupportedFeatures);
-        dest.writeInt(mSupportedRepositories);
-        dest.writeString(mServiceName);
-
-    }
-
-    @Override
-    public String toString() {
-        String ret = "Bluetooth MNS SDP Record:\n";
-
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mL2capPsm != -1) {
-            ret += "L2CAP PSM: " + mL2capPsm + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "profile version: " + mProfileVersion + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mSupportedFeatures != -1) {
-            ret += "Supported features: " + mSupportedFeatures + "\n";
-        }
-        if (mSupportedRepositories != -1) {
-            ret += "Supported repositories: " + mSupportedRepositories + "\n";
-        }
-
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpPseRecord createFromParcel(Parcel in) {
-            return new SdpPseRecord(in);
-        }
-
-        public SdpPseRecord[] newArray(int size) {
-            return new SdpPseRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/SdpRecord.java b/core/java/android/bluetooth/SdpRecord.java
deleted file mode 100644
index 730862e..0000000
--- a/core/java/android/bluetooth/SdpRecord.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* Copyright (C) 2015 Samsung System LSI
-* 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/** @hide */
-public class SdpRecord implements Parcelable {
-
-    private final byte[] mRawData;
-    private final int mRawSize;
-
-    @Override
-    public String toString() {
-        return "BluetoothSdpRecord [rawData=" + Arrays.toString(mRawData)
-                + ", rawSize=" + mRawSize + "]";
-    }
-
-    public SdpRecord(int sizeRecord, byte[] record) {
-        mRawData = record;
-        mRawSize = sizeRecord;
-    }
-
-    public SdpRecord(Parcel in) {
-        mRawSize = in.readInt();
-        mRawData = new byte[mRawSize];
-        in.readByteArray(mRawData);
-
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRawSize);
-        dest.writeByteArray(mRawData);
-
-
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpRecord createFromParcel(Parcel in) {
-            return new SdpRecord(in);
-        }
-
-        public SdpRecord[] newArray(int size) {
-            return new SdpRecord[size];
-        }
-    };
-
-    public byte[] getRawData() {
-        return mRawData;
-    }
-
-    public int getRawSize() {
-        return mRawSize;
-    }
-}
diff --git a/core/java/android/bluetooth/SdpSapsRecord.java b/core/java/android/bluetooth/SdpSapsRecord.java
deleted file mode 100644
index a1e2f7b..0000000
--- a/core/java/android/bluetooth/SdpSapsRecord.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2015 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.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class SdpSapsRecord implements Parcelable {
-    private final int mRfcommChannelNumber;
-    private final int mProfileVersion;
-    private final String mServiceName;
-
-    public SdpSapsRecord(int rfcommChannelNumber, int profileVersion, String serviceName) {
-        mRfcommChannelNumber = rfcommChannelNumber;
-        mProfileVersion = profileVersion;
-        mServiceName = serviceName;
-    }
-
-    public SdpSapsRecord(Parcel in) {
-        mRfcommChannelNumber = in.readInt();
-        mProfileVersion = in.readInt();
-        mServiceName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public int getRfcommCannelNumber() {
-        return mRfcommChannelNumber;
-    }
-
-    public int getProfileVersion() {
-        return mProfileVersion;
-    }
-
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mRfcommChannelNumber);
-        dest.writeInt(mProfileVersion);
-        dest.writeString(mServiceName);
-
-    }
-
-    @Override
-    public String toString() {
-        String ret = "Bluetooth MAS SDP Record:\n";
-
-        if (mRfcommChannelNumber != -1) {
-            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
-        }
-        if (mServiceName != null) {
-            ret += "Service Name: " + mServiceName + "\n";
-        }
-        if (mProfileVersion != -1) {
-            ret += "Profile version: " + mProfileVersion + "\n";
-        }
-        return ret;
-    }
-
-    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
-        public SdpSapsRecord createFromParcel(Parcel in) {
-            return new SdpSapsRecord(in);
-        }
-
-        public SdpRecord[] newArray(int size) {
-            return new SdpRecord[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/UidTraffic.java b/core/java/android/bluetooth/UidTraffic.java
deleted file mode 100644
index 9982fa6..0000000
--- a/core/java/android/bluetooth/UidTraffic.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2015 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.bluetooth;
-
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Record of data traffic (in bytes) by an application identified by its UID.
- *
- * @hide
- */
-@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
-public final class UidTraffic implements Cloneable, Parcelable {
-    private final int mAppUid;
-    private long mRxBytes;
-    private long mTxBytes;
-
-    /** @hide */
-    public UidTraffic(int appUid, long rx, long tx) {
-        mAppUid = appUid;
-        mRxBytes = rx;
-        mTxBytes = tx;
-    }
-
-    /** @hide */
-    private UidTraffic(Parcel in) {
-        mAppUid = in.readInt();
-        mRxBytes = in.readLong();
-        mTxBytes = in.readLong();
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mAppUid);
-        dest.writeLong(mRxBytes);
-        dest.writeLong(mTxBytes);
-    }
-
-    /** @hide */
-    public void setRxBytes(long bytes) {
-        mRxBytes = bytes;
-    }
-
-    /** @hide */
-    public void setTxBytes(long bytes) {
-        mTxBytes = bytes;
-    }
-
-    /** @hide */
-    public void addRxBytes(long bytes) {
-        mRxBytes += bytes;
-    }
-
-    /** @hide */
-    public void addTxBytes(long bytes) {
-        mTxBytes += bytes;
-    }
-
-    /**
-     * @return corresponding app Uid
-     */
-    public int getUid() {
-        return mAppUid;
-    }
-
-    /**
-     * @return rx bytes count
-     */
-    public long getRxBytes() {
-        return mRxBytes;
-    }
-
-    /**
-     * @return tx bytes count
-     */
-    public long getTxBytes() {
-        return mTxBytes;
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public UidTraffic clone() {
-        return new UidTraffic(mAppUid, mRxBytes, mTxBytes);
-    }
-
-    /** @hide */
-    @Override
-    public String toString() {
-        return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes="
-                + mTxBytes + '}';
-    }
-
-    public static final @android.annotation.NonNull Creator<UidTraffic> CREATOR = new Creator<UidTraffic>() {
-        @Override
-        public UidTraffic createFromParcel(Parcel source) {
-            return new UidTraffic(source);
-        }
-
-        @Override
-        public UidTraffic[] newArray(int size) {
-            return new UidTraffic[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
deleted file mode 100644
index c508c2c..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
- *            this requires the {@link Manifest.permission#BLUETOOTH_ADVERTISE}
- *            permission which can be gained with
- *            {@link android.app.Activity#requestPermissions(String[], int)}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothAdvertisePermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
deleted file mode 100644
index e159eaa..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
- *            this requires the {@link Manifest.permission#BLUETOOTH_CONNECT}
- *            permission which can be gained with
- *            {@link android.app.Activity#requestPermissions(String[], int)}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothConnectPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
deleted file mode 100644
index 2bb3204..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc In addition, this requires either the
- *            {@link Manifest.permission#ACCESS_FINE_LOCATION}
- *            permission or a strong assertion that you will never derive the
- *            physical location of the device. You can make this assertion by
- *            declaring {@code usesPermissionFlags="neverForLocation"} on the
- *            relevant {@code <uses-permission>} manifest tag, but it may
- *            restrict the types of Bluetooth devices you can interact with.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothLocationPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
deleted file mode 100644
index 800ff39..0000000
--- a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
- *            this requires the {@link Manifest.permission#BLUETOOTH_SCAN}
- *            permission which can be gained with
- *            {@link android.app.Activity#requestPermissions(String[], int)}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresBluetoothScanPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
deleted file mode 100644
index 9adf695..0000000
--- a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
- *            requires the {@link Manifest.permission#BLUETOOTH_ADMIN}
- *            permission which can be gained with a simple
- *            {@code <uses-permission>} manifest tag.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresLegacyBluetoothAdminPermission {
-}
diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
deleted file mode 100644
index 79621c3..0000000
--- a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.annotations;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.Manifest;
-import android.os.Build;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
- *            requires the {@link Manifest.permission#BLUETOOTH} permission
- *            which can be gained with a simple {@code <uses-permission>}
- *            manifest tag.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, FIELD})
-public @interface RequiresLegacyBluetoothPermission {
-}
diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java
deleted file mode 100644
index 4fa8c4f..0000000
--- a/core/java/android/bluetooth/le/AdvertiseCallback.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-/**
- * Bluetooth LE advertising callbacks, used to deliver advertising operation status.
- */
-public abstract class AdvertiseCallback {
-
-    /**
-     * The requested operation was successful.
-     *
-     * @hide
-     */
-    public static final int ADVERTISE_SUCCESS = 0;
-
-    /**
-     * Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.
-     */
-    public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
-
-    /**
-     * Failed to start advertising because no advertising instance is available.
-     */
-    public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
-
-    /**
-     * Failed to start advertising as the advertising is already started.
-     */
-    public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
-
-    /**
-     * Operation failed due to an internal error.
-     */
-    public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4;
-
-    /**
-     * This feature is not supported on this platform.
-     */
-    public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5;
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertising} indicating
-     * that the advertising has been started successfully.
-     *
-     * @param settingsInEffect The actual settings used for advertising, which may be different from
-     * what has been requested.
-     */
-    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
-    }
-
-    /**
-     * Callback when advertising could not be started.
-     *
-     * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for advertising start
-     * failures.
-     */
-    public void onStartFailure(int errorCode) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
deleted file mode 100644
index fdf62ec..0000000
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Advertise data packet container for Bluetooth LE advertising. This represents the data to be
- * advertised as well as the scan response data for active scans.
- * <p>
- * Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be
- * advertised.
- *
- * @see BluetoothLeAdvertiser
- * @see ScanRecord
- */
-public final class AdvertiseData implements Parcelable {
-
-    @Nullable
-    private final List<ParcelUuid> mServiceUuids;
-
-    @NonNull
-    private final List<ParcelUuid> mServiceSolicitationUuids;
-
-    @Nullable
-    private final List<TransportDiscoveryData> mTransportDiscoveryData;
-
-    private final SparseArray<byte[]> mManufacturerSpecificData;
-    private final Map<ParcelUuid, byte[]> mServiceData;
-    private final boolean mIncludeTxPowerLevel;
-    private final boolean mIncludeDeviceName;
-
-    private AdvertiseData(List<ParcelUuid> serviceUuids,
-            List<ParcelUuid> serviceSolicitationUuids,
-            List<TransportDiscoveryData> transportDiscoveryData,
-            SparseArray<byte[]> manufacturerData,
-            Map<ParcelUuid, byte[]> serviceData,
-            boolean includeTxPowerLevel,
-            boolean includeDeviceName) {
-        mServiceUuids = serviceUuids;
-        mServiceSolicitationUuids = serviceSolicitationUuids;
-        mTransportDiscoveryData = transportDiscoveryData;
-        mManufacturerSpecificData = manufacturerData;
-        mServiceData = serviceData;
-        mIncludeTxPowerLevel = includeTxPowerLevel;
-        mIncludeDeviceName = includeDeviceName;
-    }
-
-    /**
-     * Returns a list of service UUIDs within the advertisement that are used to identify the
-     * Bluetooth GATT services.
-     */
-    public List<ParcelUuid> getServiceUuids() {
-        return mServiceUuids;
-    }
-
-    /**
-     * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
-     */
-    @NonNull
-    public List<ParcelUuid> getServiceSolicitationUuids() {
-        return mServiceSolicitationUuids;
-    }
-
-    /**
-     * Returns a list of {@link TransportDiscoveryData} within the advertisement.
-     */
-    @NonNull
-    public List<TransportDiscoveryData> getTransportDiscoveryData() {
-        if (mTransportDiscoveryData == null) {
-            return Collections.emptyList();
-        }
-        return mTransportDiscoveryData;
-    }
-
-    /**
-     * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The
-     * manufacturer id is a non-negative number assigned by Bluetooth SIG.
-     */
-    public SparseArray<byte[]> getManufacturerSpecificData() {
-        return mManufacturerSpecificData;
-    }
-
-    /**
-     * Returns a map of 16-bit UUID and its corresponding service data.
-     */
-    public Map<ParcelUuid, byte[]> getServiceData() {
-        return mServiceData;
-    }
-
-    /**
-     * Whether the transmission power level will be included in the advertisement packet.
-     */
-    public boolean getIncludeTxPowerLevel() {
-        return mIncludeTxPowerLevel;
-    }
-
-    /**
-     * Whether the device name will be included in the advertisement packet.
-     */
-    public boolean getIncludeDeviceName() {
-        return mIncludeDeviceName;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int hashCode() {
-        return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData,
-                mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        AdvertiseData other = (AdvertiseData) obj;
-        return Objects.equals(mServiceUuids, other.mServiceUuids)
-                && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids)
-                && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData)
-                && BluetoothLeUtils.equals(mManufacturerSpecificData,
-                    other.mManufacturerSpecificData)
-                && BluetoothLeUtils.equals(mServiceData, other.mServiceData)
-                && mIncludeDeviceName == other.mIncludeDeviceName
-                && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel;
-    }
-
-    @Override
-    public String toString() {
-        return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids="
-                + mServiceSolicitationUuids + ", mTransportDiscoveryData="
-                + mTransportDiscoveryData + ", mManufacturerSpecificData="
-                + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData="
-                + BluetoothLeUtils.toString(mServiceData)
-                + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName="
-                + mIncludeDeviceName + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags);
-        dest.writeTypedArray(mServiceSolicitationUuids.toArray(
-                new ParcelUuid[mServiceSolicitationUuids.size()]), flags);
-
-        dest.writeTypedList(mTransportDiscoveryData);
-
-        // mManufacturerSpecificData could not be null.
-        dest.writeInt(mManufacturerSpecificData.size());
-        for (int i = 0; i < mManufacturerSpecificData.size(); ++i) {
-            dest.writeInt(mManufacturerSpecificData.keyAt(i));
-            dest.writeByteArray(mManufacturerSpecificData.valueAt(i));
-        }
-        dest.writeInt(mServiceData.size());
-        for (ParcelUuid uuid : mServiceData.keySet()) {
-            dest.writeTypedObject(uuid, flags);
-            dest.writeByteArray(mServiceData.get(uuid));
-        }
-        dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
-        dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0));
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseData> CREATOR =
-            new Creator<AdvertiseData>() {
-                @Override
-                public AdvertiseData[] newArray(int size) {
-                    return new AdvertiseData[size];
-                }
-
-                @Override
-                public AdvertiseData createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-                    ArrayList<ParcelUuid> uuids = in.createTypedArrayList(ParcelUuid.CREATOR);
-                    for (ParcelUuid uuid : uuids) {
-                        builder.addServiceUuid(uuid);
-                    }
-
-                    ArrayList<ParcelUuid> solicitationUuids = in.createTypedArrayList(ParcelUuid.CREATOR);
-                    for (ParcelUuid uuid : solicitationUuids) {
-                        builder.addServiceSolicitationUuid(uuid);
-                    }
-
-                    List<TransportDiscoveryData> transportDiscoveryData =
-                            in.createTypedArrayList(TransportDiscoveryData.CREATOR);
-                    for (TransportDiscoveryData tdd : transportDiscoveryData) {
-                        builder.addTransportDiscoveryData(tdd);
-                    }
-
-                    int manufacturerSize = in.readInt();
-                    for (int i = 0; i < manufacturerSize; ++i) {
-                        int manufacturerId = in.readInt();
-                        byte[] manufacturerData = in.createByteArray();
-                        builder.addManufacturerData(manufacturerId, manufacturerData);
-                    }
-                    int serviceDataSize = in.readInt();
-                    for (int i = 0; i < serviceDataSize; ++i) {
-                        ParcelUuid serviceDataUuid = in.readTypedObject(ParcelUuid.CREATOR);
-                        byte[] serviceData = in.createByteArray();
-                        builder.addServiceData(serviceDataUuid, serviceData);
-                    }
-                    builder.setIncludeTxPowerLevel(in.readByte() == 1);
-                    builder.setIncludeDeviceName(in.readByte() == 1);
-                    return builder.build();
-                }
-            };
-
-    /**
-     * Builder for {@link AdvertiseData}.
-     */
-    public static final class Builder {
-        @Nullable
-        private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
-        @NonNull
-        private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
-        @Nullable
-        private List<TransportDiscoveryData> mTransportDiscoveryData =
-                new ArrayList<TransportDiscoveryData>();
-        private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
-        private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
-        private boolean mIncludeTxPowerLevel;
-        private boolean mIncludeDeviceName;
-
-        /**
-         * Add a service UUID to advertise data.
-         *
-         * @param serviceUuid A service UUID to be advertised.
-         * @throws IllegalArgumentException If the {@code serviceUuid} is null.
-         */
-        public Builder addServiceUuid(ParcelUuid serviceUuid) {
-            if (serviceUuid == null) {
-                throw new IllegalArgumentException("serviceUuid is null");
-            }
-            mServiceUuids.add(serviceUuid);
-            return this;
-        }
-
-        /**
-         * Add a service solicitation UUID to advertise data.
-         *
-         * @param serviceSolicitationUuid A service solicitation UUID to be advertised.
-         * @throws IllegalArgumentException If the {@code serviceSolicitationUuid} is null.
-         */
-        @NonNull
-        public Builder addServiceSolicitationUuid(@NonNull ParcelUuid serviceSolicitationUuid) {
-            if (serviceSolicitationUuid == null) {
-                throw new IllegalArgumentException("serviceSolicitationUuid is null");
-            }
-            mServiceSolicitationUuids.add(serviceSolicitationUuid);
-            return this;
-        }
-
-        /**
-         * Add service data to advertise data.
-         *
-         * @param serviceDataUuid 16-bit UUID of the service the data is associated with
-         * @param serviceData Service data
-         * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is
-         * empty.
-         */
-        public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
-            if (serviceDataUuid == null || serviceData == null) {
-                throw new IllegalArgumentException(
-                        "serviceDataUuid or serviceDataUuid is null");
-            }
-            mServiceData.put(serviceDataUuid, serviceData);
-            return this;
-        }
-
-        /**
-         * Add Transport Discovery Data to advertise data.
-         *
-         * @param transportDiscoveryData Transport Discovery Data, consisting of one or more
-         * Transport Blocks. Transport Discovery Data AD Type Code is already included.
-         * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty
-         */
-        @NonNull
-        public Builder addTransportDiscoveryData(
-                @NonNull TransportDiscoveryData transportDiscoveryData) {
-            if (transportDiscoveryData == null) {
-                throw new IllegalArgumentException("transportDiscoveryData is null");
-            }
-            mTransportDiscoveryData.add(transportDiscoveryData);
-            return this;
-        }
-
-        /**
-         * Add manufacturer specific data.
-         * <p>
-         * Please refer to the Bluetooth Assigned Numbers document provided by the <a
-         * href="https://www.bluetooth.org">Bluetooth SIG</a> for a list of existing company
-         * identifiers.
-         *
-         * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG.
-         * @param manufacturerSpecificData Manufacturer specific data
-         * @throws IllegalArgumentException If the {@code manufacturerId} is negative or {@code
-         * manufacturerSpecificData} is null.
-         */
-        public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
-            if (manufacturerId < 0) {
-                throw new IllegalArgumentException(
-                        "invalid manufacturerId - " + manufacturerId);
-            }
-            if (manufacturerSpecificData == null) {
-                throw new IllegalArgumentException("manufacturerSpecificData is null");
-            }
-            mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData);
-            return this;
-        }
-
-        /**
-         * Whether the transmission power level should be included in the advertise packet. Tx power
-         * level field takes 3 bytes in advertise packet.
-         */
-        public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) {
-            mIncludeTxPowerLevel = includeTxPowerLevel;
-            return this;
-        }
-
-        /**
-         * Set whether the device name should be included in advertise packet.
-         */
-        public Builder setIncludeDeviceName(boolean includeDeviceName) {
-            mIncludeDeviceName = includeDeviceName;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertiseData}.
-         */
-        public AdvertiseData build() {
-            return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids,
-                    mTransportDiscoveryData, mManufacturerSpecificData, mServiceData,
-                    mIncludeTxPowerLevel, mIncludeDeviceName);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
deleted file mode 100644
index c52a6ee..0000000
--- a/core/java/android/bluetooth/le/AdvertiseSettings.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each
- * Bluetooth LE advertisement instance. Use {@link AdvertiseSettings.Builder} to create an
- * instance of this class.
- */
-public final class AdvertiseSettings implements Parcelable {
-    /**
-     * Perform Bluetooth LE advertising in low power mode. This is the default and preferred
-     * advertising mode as it consumes the least power.
-     */
-    public static final int ADVERTISE_MODE_LOW_POWER = 0;
-
-    /**
-     * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising
-     * frequency and power consumption.
-     */
-    public static final int ADVERTISE_MODE_BALANCED = 1;
-
-    /**
-     * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power
-     * consumption and should not be used for continuous background advertising.
-     */
-    public static final int ADVERTISE_MODE_LOW_LATENCY = 2;
-
-    /**
-     * Advertise using the lowest transmission (TX) power level. Low transmission power can be used
-     * to restrict the visibility range of advertising packets.
-     */
-    public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0;
-
-    /**
-     * Advertise using low TX power level.
-     */
-    public static final int ADVERTISE_TX_POWER_LOW = 1;
-
-    /**
-     * Advertise using medium TX power level.
-     */
-    public static final int ADVERTISE_TX_POWER_MEDIUM = 2;
-
-    /**
-     * Advertise using high TX power level. This corresponds to largest visibility range of the
-     * advertising packet.
-     */
-    public static final int ADVERTISE_TX_POWER_HIGH = 3;
-
-    /**
-     * The maximum limited advertisement duration as specified by the Bluetooth SIG
-     */
-    private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
-
-
-    private final int mAdvertiseMode;
-    private final int mAdvertiseTxPowerLevel;
-    private final int mAdvertiseTimeoutMillis;
-    private final boolean mAdvertiseConnectable;
-    private final int mOwnAddressType;
-
-    private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
-            boolean advertiseConnectable, int advertiseTimeout,
-            @AddressTypeStatus int ownAddressType) {
-        mAdvertiseMode = advertiseMode;
-        mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
-        mAdvertiseConnectable = advertiseConnectable;
-        mAdvertiseTimeoutMillis = advertiseTimeout;
-        mOwnAddressType = ownAddressType;
-    }
-
-    private AdvertiseSettings(Parcel in) {
-        mAdvertiseMode = in.readInt();
-        mAdvertiseTxPowerLevel = in.readInt();
-        mAdvertiseConnectable = in.readInt() != 0;
-        mAdvertiseTimeoutMillis = in.readInt();
-        mOwnAddressType = in.readInt();
-    }
-
-    /**
-     * Returns the advertise mode.
-     */
-    public int getMode() {
-        return mAdvertiseMode;
-    }
-
-    /**
-     * Returns the TX power level for advertising.
-     */
-    public int getTxPowerLevel() {
-        return mAdvertiseTxPowerLevel;
-    }
-
-    /**
-     * Returns whether the advertisement will indicate connectable.
-     */
-    public boolean isConnectable() {
-        return mAdvertiseConnectable;
-    }
-
-    /**
-     * Returns the advertising time limit in milliseconds.
-     */
-    public int getTimeout() {
-        return mAdvertiseTimeoutMillis;
-    }
-
-    /**
-     * @return the own address type for advertising
-     *
-     * @hide
-     */
-    @SystemApi
-    public @AddressTypeStatus int getOwnAddressType() {
-        return mOwnAddressType;
-    }
-
-    @Override
-    public String toString() {
-        return "Settings [mAdvertiseMode=" + mAdvertiseMode
-                + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel
-                + ", mAdvertiseConnectable=" + mAdvertiseConnectable
-                + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis
-                + ", mOwnAddressType=" + mOwnAddressType + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mAdvertiseMode);
-        dest.writeInt(mAdvertiseTxPowerLevel);
-        dest.writeInt(mAdvertiseConnectable ? 1 : 0);
-        dest.writeInt(mAdvertiseTimeoutMillis);
-        dest.writeInt(mOwnAddressType);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR =
-            new Creator<AdvertiseSettings>() {
-                @Override
-                public AdvertiseSettings[] newArray(int size) {
-                    return new AdvertiseSettings[size];
-                }
-
-                @Override
-                public AdvertiseSettings createFromParcel(Parcel in) {
-                    return new AdvertiseSettings(in);
-                }
-            };
-
-    /**
-     * Builder class for {@link AdvertiseSettings}.
-     */
-    public static final class Builder {
-        private int mMode = ADVERTISE_MODE_LOW_POWER;
-        private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
-        private int mTimeoutMillis = 0;
-        private boolean mConnectable = true;
-        private int mOwnAddressType = AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT;
-
-        /**
-         * Set advertise mode to control the advertising power and latency.
-         *
-         * @param advertiseMode Bluetooth LE Advertising mode, can only be one of {@link
-         * AdvertiseSettings#ADVERTISE_MODE_LOW_POWER},
-         * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED},
-         * or {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}.
-         * @throws IllegalArgumentException If the advertiseMode is invalid.
-         */
-        public Builder setAdvertiseMode(int advertiseMode) {
-            if (advertiseMode < ADVERTISE_MODE_LOW_POWER
-                    || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) {
-                throw new IllegalArgumentException("unknown mode " + advertiseMode);
-            }
-            mMode = advertiseMode;
-            return this;
-        }
-
-        /**
-         * Set advertise TX power level to control the transmission power level for the advertising.
-         *
-         * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of
-         * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, {@link
-         * AdvertiseSettings#ADVERTISE_TX_POWER_LOW},
-         * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM}
-         * or {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}.
-         * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
-         */
-        public Builder setTxPowerLevel(int txPowerLevel) {
-            if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW
-                    || txPowerLevel > ADVERTISE_TX_POWER_HIGH) {
-                throw new IllegalArgumentException("unknown tx power level " + txPowerLevel);
-            }
-            mTxPowerLevel = txPowerLevel;
-            return this;
-        }
-
-        /**
-         * Set whether the advertisement type should be connectable or non-connectable.
-         *
-         * @param connectable Controls whether the advertisment type will be connectable (true) or
-         * non-connectable (false).
-         */
-        public Builder setConnectable(boolean connectable) {
-            mConnectable = connectable;
-            return this;
-        }
-
-        /**
-         * Limit advertising to a given amount of time.
-         *
-         * @param timeoutMillis Advertising time limit. May not exceed 180000 milliseconds. A value
-         * of 0 will disable the time limit.
-         * @throws IllegalArgumentException If the provided timeout is over 180000 ms.
-         */
-        public Builder setTimeout(int timeoutMillis) {
-            if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) {
-                throw new IllegalArgumentException("timeoutMillis invalid (must be 0-"
-                        + LIMITED_ADVERTISING_MAX_MILLIS + " milliseconds)");
-            }
-            mTimeoutMillis = timeoutMillis;
-            return this;
-        }
-
-        /**
-         * Set own address type for advertising to control public or privacy mode. If used to set
-         * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
-         * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
-         * time of starting advertising.
-         *
-         * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
-         *
-         * @hide
-         */
-        @SystemApi
-        public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
-            if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
-                    ||  ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
-                throw new IllegalArgumentException("unknown address type " + ownAddressType);
-            }
-            mOwnAddressType = ownAddressType;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertiseSettings} object.
-         */
-        public AdvertiseSettings build() {
-            return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis,
-                mOwnAddressType);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
deleted file mode 100644
index bbdb695..0000000
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * This class provides a way to control single Bluetooth LE advertising instance.
- * <p>
- * To get an instance of {@link AdvertisingSet}, call the
- * {@link BluetoothLeAdvertiser#startAdvertisingSet} method.
- *
- * @see AdvertiseData
- */
-public final class AdvertisingSet {
-    private static final String TAG = "AdvertisingSet";
-
-    private final IBluetoothGatt mGatt;
-    private int mAdvertiserId;
-    private AttributionSource mAttributionSource;
-
-    /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager,
-            AttributionSource attributionSource) {
-        mAdvertiserId = advertiserId;
-        mAttributionSource = attributionSource;
-        try {
-            mGatt = bluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-            throw new IllegalStateException("Failed to get Bluetooth");
-        }
-    }
-
-    /* package */ void setAdvertiserId(int advertiserId) {
-        mAdvertiserId = advertiserId;
-    }
-
-    /**
-     * Enables Advertising. This method returns immediately, the operation status is
-     * delivered through {@code callback.onAdvertisingEnabled()}.
-     *
-     * @param enable whether the advertising should be enabled (true), or disabled (false)
-     * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
-     * (655,350 ms)
-     * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the
-     * controller shall attempt to send prior to terminating the extended advertising, even if the
-     * duration has not expired. Valid range is from 1 to 255.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void enableAdvertising(boolean enable, int duration,
-            int maxExtendedAdvertisingEvents) {
-        try {
-            mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration,
-                    maxExtendedAdvertisingEvents, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for
-     * specified AdvertisingSetParameters. This method returns immediately, the operation status is
-     * delivered through {@code callback.onAdvertisingDataSet()}.
-     * <p>
-     * Advertising data must be empty if non-legacy scannable advertising is used.
-     *
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags. If the update takes place when the advertising set is
-     * enabled, the data can be maximum 251 bytes long.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setAdvertisingData(AdvertiseData advertiseData) {
-        try {
-            mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Set/update scan response data. Make sure that data doesn't exceed the size limit for
-     * specified AdvertisingSetParameters. This method returns immediately, the operation status
-     * is delivered through {@code callback.onScanResponseDataSet()}.
-     *
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place
-     * when the advertising set is enabled, the data can be maximum 251 bytes long.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setScanResponseData(AdvertiseData scanResponse) {
-        try {
-            mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Update advertising parameters associated with this AdvertisingSet. Must be called when
-     * advertising is not active. This method returns immediately, the operation status is delivered
-     * through {@code callback.onAdvertisingParametersUpdated}.
-     *
-     * @param parameters advertising set parameters.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
-        try {
-            mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Update periodic advertising parameters associated with this set. Must be called when
-     * periodic advertising is not enabled. This method returns immediately, the operation
-     * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
-        try {
-            mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters,
-     * or after advertising was started with periodic advertising data set. This method returns
-     * immediately, the operation status is delivered through
-     * {@code callback.onPeriodicAdvertisingDataSet()}.
-     *
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the
-     * periodic advertising is enabled for this set, the data can be maximum 251 bytes long.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setPeriodicAdvertisingData(AdvertiseData periodicData) {
-        try {
-            mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Used to enable/disable periodic advertising. This method returns immediately, the operation
-     * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}.
-     *
-     * @param enable whether the periodic advertising should be enabled (true), or disabled
-     * (false).
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void setPeriodicAdvertisingEnabled(boolean enable) {
-        try {
-            mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Returns address associated with this advertising set.
-     * This method is exposed only for Bluetooth PTS tests, no app or system service
-     * should ever use it.
-     *
-     * @hide
-     */
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_ADVERTISE,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public void getOwnAddress() {
-        try {
-            mGatt.getOwnAddress(mAdvertiserId, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "remote exception - ", e);
-        }
-    }
-
-    /**
-     * Returns advertiserId associated with this advertising set.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public int getAdvertiserId() {
-        return mAdvertiserId;
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetCallback.java b/core/java/android/bluetooth/le/AdvertisingSetCallback.java
deleted file mode 100644
index 51324fd..0000000
--- a/core/java/android/bluetooth/le/AdvertisingSetCallback.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-/**
- * Bluetooth LE advertising set callbacks, used to deliver advertising operation
- * status.
- */
-public abstract class AdvertisingSetCallback {
-
-    /**
-     * The requested operation was successful.
-     */
-    public static final int ADVERTISE_SUCCESS = 0;
-
-    /**
-     * Failed to start advertising as the advertise data to be broadcasted is too
-     * large.
-     */
-    public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
-
-    /**
-     * Failed to start advertising because no advertising instance is available.
-     */
-    public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
-
-    /**
-     * Failed to start advertising as the advertising is already started.
-     */
-    public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
-
-    /**
-     * Operation failed due to an internal error.
-     */
-    public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4;
-
-    /**
-     * This feature is not supported on this platform.
-     */
-    public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5;
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
-     * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet
-     * contains the started set and it is advertising. If error occurred, advertisingSet is
-     * null, and status will be set to proper error code.
-     *
-     * @param advertisingSet The advertising set that was started or null if error.
-     * @param txPower tx power that will be used for this set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet}
-     * indicating advertising set is stopped.
-     *
-     * @param advertisingSet The advertising set.
-     */
-    public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
-    }
-
-    /**
-     * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
-     * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is
-     * advertising.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
-     * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
-     * result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param txPower tx power that will be used for this set.
-     * @param status Status of the operation.
-     */
-    public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
-            int txPower, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet,
-            int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnabled}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param status Status of the operation.
-     */
-    public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
-            int status) {
-    }
-
-    /**
-     * Callback triggered in response to {@link AdvertisingSet#getOwnAddress()}
-     * indicating result of the operation.
-     *
-     * @param advertisingSet The advertising set.
-     * @param addressType type of address.
-     * @param address advertising set bluetooth address.
-     * @hide
-     */
-    public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
deleted file mode 100644
index 5c8fae6..0000000
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * The {@link AdvertisingSetParameters} provide a way to adjust advertising
- * preferences for each
- * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to
- * create an
- * instance of this class.
- */
-public final class AdvertisingSetParameters implements Parcelable {
-
-    /**
-     * Advertise on low frequency, around every 1000ms. This is the default and
-     * preferred advertising mode as it consumes the least power.
-     */
-    public static final int INTERVAL_HIGH = 1600;
-
-    /**
-     * Advertise on medium frequency, around every 250ms. This is balanced
-     * between advertising frequency and power consumption.
-     */
-    public static final int INTERVAL_MEDIUM = 400;
-
-    /**
-     * Perform high frequency, low latency advertising, around every 100ms. This
-     * has the highest power consumption and should not be used for continuous
-     * background advertising.
-     */
-    public static final int INTERVAL_LOW = 160;
-
-    /**
-     * Minimum value for advertising interval.
-     */
-    public static final int INTERVAL_MIN = 160;
-
-    /**
-     * Maximum value for advertising interval.
-     */
-    public static final int INTERVAL_MAX = 16777215;
-
-    /**
-     * Advertise using the lowest transmission (TX) power level. Low transmission
-     * power can be used to restrict the visibility range of advertising packets.
-     */
-    public static final int TX_POWER_ULTRA_LOW = -21;
-
-    /**
-     * Advertise using low TX power level.
-     */
-    public static final int TX_POWER_LOW = -15;
-
-    /**
-     * Advertise using medium TX power level.
-     */
-    public static final int TX_POWER_MEDIUM = -7;
-
-    /**
-     * Advertise using high TX power level. This corresponds to largest visibility
-     * range of the advertising packet.
-     */
-    public static final int TX_POWER_HIGH = 1;
-
-    /**
-     * Minimum value for TX power.
-     */
-    public static final int TX_POWER_MIN = -127;
-
-    /**
-     * Maximum value for TX power.
-     */
-    public static final int TX_POWER_MAX = 1;
-
-    /**
-     * The maximum limited advertisement duration as specified by the Bluetooth
-     * SIG
-     */
-    private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
-
-    /** @hide */
-    @IntDef(prefix = "ADDRESS_TYPE_", value = {
-        ADDRESS_TYPE_DEFAULT,
-        ADDRESS_TYPE_PUBLIC,
-        ADDRESS_TYPE_RANDOM
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AddressTypeStatus {}
-
-    /**
-     * Advertise own address type that corresponds privacy settings of the device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ADDRESS_TYPE_DEFAULT = -1;
-
-    /**
-     * Advertise own public address type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ADDRESS_TYPE_PUBLIC = 0;
-
-    /**
-     * Generate and adverise own resolvable private address.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int ADDRESS_TYPE_RANDOM = 1;
-
-    private final boolean mIsLegacy;
-    private final boolean mIsAnonymous;
-    private final boolean mIncludeTxPower;
-    private final int mPrimaryPhy;
-    private final int mSecondaryPhy;
-    private final boolean mConnectable;
-    private final boolean mScannable;
-    private final int mInterval;
-    private final int mTxPowerLevel;
-    private final int mOwnAddressType;
-
-    private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy,
-            boolean isAnonymous, boolean includeTxPower,
-            int primaryPhy, int secondaryPhy,
-            int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType) {
-        mConnectable = connectable;
-        mScannable = scannable;
-        mIsLegacy = isLegacy;
-        mIsAnonymous = isAnonymous;
-        mIncludeTxPower = includeTxPower;
-        mPrimaryPhy = primaryPhy;
-        mSecondaryPhy = secondaryPhy;
-        mInterval = interval;
-        mTxPowerLevel = txPowerLevel;
-        mOwnAddressType = ownAddressType;
-    }
-
-    private AdvertisingSetParameters(Parcel in) {
-        mConnectable = in.readInt() != 0;
-        mScannable = in.readInt() != 0;
-        mIsLegacy = in.readInt() != 0;
-        mIsAnonymous = in.readInt() != 0;
-        mIncludeTxPower = in.readInt() != 0;
-        mPrimaryPhy = in.readInt();
-        mSecondaryPhy = in.readInt();
-        mInterval = in.readInt();
-        mTxPowerLevel = in.readInt();
-        mOwnAddressType = in.readInt();
-    }
-
-    /**
-     * Returns whether the advertisement will be connectable.
-     */
-    public boolean isConnectable() {
-        return mConnectable;
-    }
-
-    /**
-     * Returns whether the advertisement will be scannable.
-     */
-    public boolean isScannable() {
-        return mScannable;
-    }
-
-    /**
-     * Returns whether the legacy advertisement will be used.
-     */
-    public boolean isLegacy() {
-        return mIsLegacy;
-    }
-
-    /**
-     * Returns whether the advertisement will be anonymous.
-     */
-    public boolean isAnonymous() {
-        return mIsAnonymous;
-    }
-
-    /**
-     * Returns whether the TX Power will be included.
-     */
-    public boolean includeTxPower() {
-        return mIncludeTxPower;
-    }
-
-    /**
-     * Returns the primary advertising phy.
-     */
-    public int getPrimaryPhy() {
-        return mPrimaryPhy;
-    }
-
-    /**
-     * Returns the secondary advertising phy.
-     */
-    public int getSecondaryPhy() {
-        return mSecondaryPhy;
-    }
-
-    /**
-     * Returns the advertising interval.
-     */
-    public int getInterval() {
-        return mInterval;
-    }
-
-    /**
-     * Returns the TX power level for advertising.
-     */
-    public int getTxPowerLevel() {
-        return mTxPowerLevel;
-    }
-
-    /**
-     * @return the own address type for advertising
-     *
-     * @hide
-     */
-    @SystemApi
-    public @AddressTypeStatus int getOwnAddressType() {
-        return mOwnAddressType;
-    }
-
-    @Override
-    public String toString() {
-        return "AdvertisingSetParameters [connectable=" + mConnectable
-                + ", isLegacy=" + mIsLegacy
-                + ", isAnonymous=" + mIsAnonymous
-                + ", includeTxPower=" + mIncludeTxPower
-                + ", primaryPhy=" + mPrimaryPhy
-                + ", secondaryPhy=" + mSecondaryPhy
-                + ", interval=" + mInterval
-                + ", txPowerLevel=" + mTxPowerLevel
-                + ", ownAddressType=" + mOwnAddressType + "]";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mConnectable ? 1 : 0);
-        dest.writeInt(mScannable ? 1 : 0);
-        dest.writeInt(mIsLegacy ? 1 : 0);
-        dest.writeInt(mIsAnonymous ? 1 : 0);
-        dest.writeInt(mIncludeTxPower ? 1 : 0);
-        dest.writeInt(mPrimaryPhy);
-        dest.writeInt(mSecondaryPhy);
-        dest.writeInt(mInterval);
-        dest.writeInt(mTxPowerLevel);
-        dest.writeInt(mOwnAddressType);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR =
-            new Creator<AdvertisingSetParameters>() {
-                @Override
-                public AdvertisingSetParameters[] newArray(int size) {
-                    return new AdvertisingSetParameters[size];
-                }
-
-                @Override
-                public AdvertisingSetParameters createFromParcel(Parcel in) {
-                    return new AdvertisingSetParameters(in);
-                }
-            };
-
-    /**
-     * Builder class for {@link AdvertisingSetParameters}.
-     */
-    public static final class Builder {
-        private boolean mConnectable = false;
-        private boolean mScannable = false;
-        private boolean mIsLegacy = false;
-        private boolean mIsAnonymous = false;
-        private boolean mIncludeTxPower = false;
-        private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
-        private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
-        private int mInterval = INTERVAL_LOW;
-        private int mTxPowerLevel = TX_POWER_MEDIUM;
-        private int mOwnAddressType = ADDRESS_TYPE_DEFAULT;
-
-        /**
-         * Set whether the advertisement type should be connectable or
-         * non-connectable.
-         * Legacy advertisements can be both connectable and scannable. Non-legacy
-         * advertisements can be only scannable or only connectable.
-         *
-         * @param connectable Controls whether the advertisement type will be connectable (true) or
-         * non-connectable (false).
-         */
-        public Builder setConnectable(boolean connectable) {
-            mConnectable = connectable;
-            return this;
-        }
-
-        /**
-         * Set whether the advertisement type should be scannable.
-         * Legacy advertisements can be both connectable and scannable. Non-legacy
-         * advertisements can be only scannable or only connectable.
-         *
-         * @param scannable Controls whether the advertisement type will be scannable (true) or
-         * non-scannable (false).
-         */
-        public Builder setScannable(boolean scannable) {
-            mScannable = scannable;
-            return this;
-        }
-
-        /**
-         * When set to true, advertising set will advertise 4.x Spec compliant
-         * advertisements.
-         *
-         * @param isLegacy whether legacy advertising mode should be used.
-         */
-        public Builder setLegacyMode(boolean isLegacy) {
-            mIsLegacy = isLegacy;
-            return this;
-        }
-
-        /**
-         * Set whether advertiser address should be ommited from all packets. If this
-         * mode is used, periodic advertising can't be enabled for this set.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * @param isAnonymous whether anonymous advertising should be used.
-         */
-        public Builder setAnonymous(boolean isAnonymous) {
-            mIsAnonymous = isAnonymous;
-            return this;
-        }
-
-        /**
-         * Set whether TX power should be included in the extended header.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * @param includeTxPower whether TX power should be included in extended header
-         */
-        public Builder setIncludeTxPower(boolean includeTxPower) {
-            mIncludeTxPower = includeTxPower;
-            return this;
-        }
-
-        /**
-         * Set the primary physical channel used for this advertising set.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is
-         * supported on this device.
-         *
-         * @param primaryPhy Primary advertising physical channel, can only be {@link
-         * BluetoothDevice#PHY_LE_1M} or {@link BluetoothDevice#PHY_LE_CODED}.
-         * @throws IllegalArgumentException If the primaryPhy is invalid.
-         */
-        public Builder setPrimaryPhy(int primaryPhy) {
-            if (primaryPhy != BluetoothDevice.PHY_LE_1M
-                    && primaryPhy != BluetoothDevice.PHY_LE_CODED) {
-                throw new IllegalArgumentException("bad primaryPhy " + primaryPhy);
-            }
-            mPrimaryPhy = primaryPhy;
-            return this;
-        }
-
-        /**
-         * Set the secondary physical channel used for this advertising set.
-         *
-         * This is used only if legacy mode is not used.
-         *
-         * Use {@link BluetoothAdapter#isLeCodedPhySupported} and
-         * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is
-         * supported on this device.
-         *
-         * @param secondaryPhy Secondary advertising physical channel, can only be one of {@link
-         * BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M} or {@link
-         * BluetoothDevice#PHY_LE_CODED}.
-         * @throws IllegalArgumentException If the secondaryPhy is invalid.
-         */
-        public Builder setSecondaryPhy(int secondaryPhy) {
-            if (secondaryPhy != BluetoothDevice.PHY_LE_1M
-                    && secondaryPhy != BluetoothDevice.PHY_LE_2M
-                    && secondaryPhy != BluetoothDevice.PHY_LE_CODED) {
-                throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy);
-            }
-            mSecondaryPhy = secondaryPhy;
-            return this;
-        }
-
-        /**
-         * Set advertising interval.
-         *
-         * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid range is from
-         * 160 (100ms) to 16777215 (10,485.759375 s). Recommended values are: {@link
-         * AdvertisingSetParameters#INTERVAL_LOW}, {@link AdvertisingSetParameters#INTERVAL_MEDIUM},
-         * or {@link AdvertisingSetParameters#INTERVAL_HIGH}.
-         * @throws IllegalArgumentException If the interval is invalid.
-         */
-        public Builder setInterval(int interval) {
-            if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
-                throw new IllegalArgumentException("unknown interval " + interval);
-            }
-            mInterval = interval;
-            return this;
-        }
-
-        /**
-         * Set the transmission power level for the advertising.
-         *
-         * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in dBm. The valid
-         * range is [-127, 1] Recommended values are:
-         * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW},
-         * {@link AdvertisingSetParameters#TX_POWER_LOW},
-         * {@link AdvertisingSetParameters#TX_POWER_MEDIUM},
-         * or {@link AdvertisingSetParameters#TX_POWER_HIGH}.
-         * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
-         */
-        public Builder setTxPowerLevel(int txPowerLevel) {
-            if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
-                throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel);
-            }
-            mTxPowerLevel = txPowerLevel;
-            return this;
-        }
-
-        /**
-         * Set own address type for advertising to control public or privacy mode. If used to set
-         * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
-         * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
-         * time of starting advertising.
-         *
-         * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
-         *
-         * @hide
-         */
-        @SystemApi
-        public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
-            if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
-                    ||  ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
-                throw new IllegalArgumentException("unknown address type " + ownAddressType);
-            }
-            mOwnAddressType = ownAddressType;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertisingSetParameters} object.
-         *
-         * @throws IllegalStateException if invalid combination of parameters is used.
-         */
-        public AdvertisingSetParameters build() {
-            if (mIsLegacy) {
-                if (mIsAnonymous) {
-                    throw new IllegalArgumentException("Legacy advertising can't be anonymous");
-                }
-
-                if (mConnectable && !mScannable) {
-                    throw new IllegalStateException(
-                            "Legacy advertisement can't be connectable and non-scannable");
-                }
-
-                if (mIncludeTxPower) {
-                    throw new IllegalStateException(
-                            "Legacy advertising can't include TX power level in header");
-                }
-            } else {
-                if (mConnectable && mScannable) {
-                    throw new IllegalStateException(
-                            "Advertising can't be both connectable and scannable");
-                }
-
-                if (mIsAnonymous && mConnectable) {
-                    throw new IllegalStateException(
-                            "Advertising can't be both connectable and anonymous");
-                }
-            }
-
-            return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous,
-                    mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel,
-                    mOwnAddressType);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
deleted file mode 100644
index 879dcee..0000000
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class provides a way to perform Bluetooth LE advertise operations, such as starting and
- * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data
- * represented by {@link AdvertiseData}.
- * <p>
- * To get an instance of {@link BluetoothLeAdvertiser}, call the
- * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
- *
- * @see AdvertiseData
- */
-public final class BluetoothLeAdvertiser {
-
-    private static final String TAG = "BluetoothLeAdvertiser";
-
-    private static final int MAX_ADVERTISING_DATA_BYTES = 1650;
-    private static final int MAX_LEGACY_ADVERTISING_DATA_BYTES = 31;
-    // Each fields need one byte for field length and another byte for field type.
-    private static final int OVERHEAD_BYTES_PER_FIELD = 2;
-    // Flags field will be set by system.
-    private static final int FLAGS_FIELD_BYTES = 3;
-    private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2;
-
-    private final BluetoothAdapter mBluetoothAdapter;
-    private final IBluetoothManager mBluetoothManager;
-    private final AttributionSource mAttributionSource;
-
-    private final Handler mHandler;
-    private final Map<AdvertiseCallback, AdvertisingSetCallback>
-            mLegacyAdvertisers = new HashMap<>();
-    private final Map<AdvertisingSetCallback, IAdvertisingSetCallback>
-            mCallbackWrappers = Collections.synchronizedMap(new HashMap<>());
-    private final Map<Integer, AdvertisingSet>
-            mAdvertisingSets = Collections.synchronizedMap(new HashMap<>());
-
-    /**
-     * Use BluetoothAdapter.getLeAdvertiser() instead.
-     *
-     * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management
-     * @hide
-     */
-    public BluetoothLeAdvertiser(BluetoothAdapter bluetoothAdapter) {
-        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
-        mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
-        mAttributionSource = mBluetoothAdapter.getAttributionSource();
-        mHandler = new Handler(Looper.getMainLooper());
-    }
-
-    /**
-     * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted.
-     * Returns immediately, the operation status is delivered through {@code callback}.
-     *
-     * @param settings Settings for Bluetooth LE advertising.
-     * @param advertiseData Advertisement data to be broadcasted.
-     * @param callback Callback for advertising status.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertising(AdvertiseSettings settings,
-            AdvertiseData advertiseData, final AdvertiseCallback callback) {
-        startAdvertising(settings, advertiseData, null, callback);
-    }
-
-    /**
-     * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the
-     * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an
-     * active scan request. This method returns immediately, the operation status is delivered
-     * through {@code callback}.
-     *
-     * @param settings Settings for Bluetooth LE advertising.
-     * @param advertiseData Advertisement data to be advertised in advertisement packet.
-     * @param scanResponse Scan response associated with the advertisement data.
-     * @param callback Callback for advertising status.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertising(AdvertiseSettings settings,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            final AdvertiseCallback callback) {
-        synchronized (mLegacyAdvertisers) {
-            BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-            if (callback == null) {
-                throw new IllegalArgumentException("callback cannot be null");
-            }
-            boolean isConnectable = settings.isConnectable();
-            if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES
-                    || totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
-                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
-                return;
-            }
-            if (mLegacyAdvertisers.containsKey(callback)) {
-                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
-                return;
-            }
-
-            AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder();
-            parameters.setLegacyMode(true);
-            parameters.setConnectable(isConnectable);
-            parameters.setScannable(true); // legacy advertisements we support are always scannable
-            parameters.setOwnAddressType(settings.getOwnAddressType());
-            if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
-                parameters.setInterval(1600); // 1s
-            } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
-                parameters.setInterval(400); // 250ms
-            } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) {
-                parameters.setInterval(160); // 100ms
-            }
-
-            if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW) {
-                parameters.setTxPowerLevel(-21);
-            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_LOW) {
-                parameters.setTxPowerLevel(-15);
-            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) {
-                parameters.setTxPowerLevel(-7);
-            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) {
-                parameters.setTxPowerLevel(1);
-            }
-
-            int duration = 0;
-            int timeoutMillis = settings.getTimeout();
-            if (timeoutMillis > 0) {
-                duration = (timeoutMillis < 10) ? 1 : timeoutMillis / 10;
-            }
-
-            AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings);
-            mLegacyAdvertisers.put(callback, wrapped);
-            startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null,
-                    duration, 0, wrapped);
-        }
-    }
-
-    @SuppressLint({
-            "AndroidFrameworkBluetoothPermission",
-            "AndroidFrameworkRequiresPermission",
-    })
-    AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) {
-        return new AdvertisingSetCallback() {
-            @Override
-            public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
-                    int status) {
-                if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
-                    postStartFailure(callback, status);
-                    return;
-                }
-
-                postStartSuccess(callback, settings);
-            }
-
-            /* Legacy advertiser is disabled on timeout */
-            @Override
-            public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled,
-                    int status) {
-                if (enabled) {
-                    Log.e(TAG, "Legacy advertiser should be only disabled on timeout,"
-                            + " but was enabled!");
-                    return;
-                }
-
-                stopAdvertising(callback);
-            }
-
-        };
-    }
-
-    /**
-     * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
-     * {@link BluetoothLeAdvertiser#startAdvertising}.
-     *
-     * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void stopAdvertising(final AdvertiseCallback callback) {
-        synchronized (mLegacyAdvertisers) {
-            if (callback == null) {
-                throw new IllegalArgumentException("callback cannot be null");
-            }
-            AdvertisingSetCallback wrapper = mLegacyAdvertisers.get(callback);
-            if (wrapper == null) return;
-
-            stopAdvertisingSet(wrapper);
-
-            mLegacyAdvertisers.remove(callback);
-        }
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param callback Callback for advertising set.
-     * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, AdvertisingSetCallback callback) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                periodicData, 0, 0, callback, new Handler(Looper.getMainLooper()));
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param callback Callback for advertising set.
-     * @param handler thread upon which the callbacks will be invoked.
-     * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, AdvertisingSetCallback callback,
-            Handler handler) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                periodicData, 0, 0, callback, handler);
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}.
-     * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
-     * (655,350 ms). 0 means advertising should continue until stopped.
-     * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the
-     * controller shall attempt to send prior to terminating the extended advertising, even if the
-     * duration has not expired. Valid range is from 1 to 255. 0 means no maximum.
-     * @param callback Callback for advertising set.
-     * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, int duration,
-            int maxExtendedAdvertisingEvents,
-            AdvertisingSetCallback callback) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                periodicData, duration, maxExtendedAdvertisingEvents, callback,
-                new Handler(Looper.getMainLooper()));
-    }
-
-    /**
-     * Creates a new advertising set. If operation succeed, device will start advertising. This
-     * method returns immediately, the operation status is delivered through
-     * {@code callback.onAdvertisingSetStarted()}.
-     * <p>
-     *
-     * @param parameters Advertising set parameters.
-     * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable,
-     * three bytes will be added for flags.
-     * @param scanResponse Scan response associated with the advertisement data. Size must not
-     * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}
-     * @param periodicParameters Periodic advertisng parameters. If null, periodic advertising will
-     * not be started.
-     * @param periodicData Periodic advertising data. Size must not exceed {@link
-     * BluetoothAdapter#getLeMaximumAdvertisingDataLength}
-     * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
-     * (655,350 ms). 0 means advertising should continue until stopped.
-     * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the
-     * controller shall attempt to send prior to terminating the extended advertising, even if the
-     * duration has not expired. Valid range is from 1 to 255. 0 means no maximum.
-     * @param callback Callback for advertising set.
-     * @param handler Thread upon which the callbacks will be invoked.
-     * @throws IllegalArgumentException When any of the data parameter exceed the maximum allowable
-     * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
-     * feature is made when it's not supported by the controller, or when
-     * maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended
-     * Advertising
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void startAdvertisingSet(AdvertisingSetParameters parameters,
-            AdvertiseData advertiseData, AdvertiseData scanResponse,
-            PeriodicAdvertisingParameters periodicParameters,
-            AdvertiseData periodicData, int duration,
-            int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback,
-            Handler handler) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-
-        boolean isConnectable = parameters.isConnectable();
-        if (parameters.isLegacy()) {
-            if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
-                throw new IllegalArgumentException("Legacy advertising data too big");
-            }
-
-            if (totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) {
-                throw new IllegalArgumentException("Legacy scan response data too big");
-            }
-        } else {
-            boolean supportCodedPhy = mBluetoothAdapter.isLeCodedPhySupported();
-            boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported();
-            int pphy = parameters.getPrimaryPhy();
-            int sphy = parameters.getSecondaryPhy();
-            if (pphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) {
-                throw new IllegalArgumentException("Unsupported primary PHY selected");
-            }
-
-            if ((sphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy)
-                    || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) {
-                throw new IllegalArgumentException("Unsupported secondary PHY selected");
-            }
-
-            int maxData = mBluetoothAdapter.getLeMaximumAdvertisingDataLength();
-            if (totalBytes(advertiseData, isConnectable) > maxData) {
-                throw new IllegalArgumentException("Advertising data too big");
-            }
-
-            if (totalBytes(scanResponse, false) > maxData) {
-                throw new IllegalArgumentException("Scan response data too big");
-            }
-
-            if (totalBytes(periodicData, false) > maxData) {
-                throw new IllegalArgumentException("Periodic advertising data too big");
-            }
-
-            boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported();
-            if (periodicParameters != null && !supportPeriodic) {
-                throw new IllegalArgumentException(
-                        "Controller does not support LE Periodic Advertising");
-            }
-        }
-
-        if (maxExtendedAdvertisingEvents < 0 || maxExtendedAdvertisingEvents > 255) {
-            throw new IllegalArgumentException(
-                    "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents);
-        }
-
-        if (maxExtendedAdvertisingEvents != 0
-                && !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) {
-            throw new IllegalArgumentException(
-                    "Can't use maxExtendedAdvertisingEvents with controller that don't support "
-                            + "LE Extended Advertising");
-        }
-
-        if (duration < 0 || duration > 65535) {
-            throw new IllegalArgumentException("duration out of range: " + duration);
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth GATT - ", e);
-            postStartSetFailure(handler, callback,
-                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-            return;
-        }
-
-        if (gatt == null) {
-            Log.e(TAG, "Bluetooth GATT is null");
-            postStartSetFailure(handler, callback,
-                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-            return;
-        }
-
-        IAdvertisingSetCallback wrapped = wrap(callback, handler);
-        if (mCallbackWrappers.putIfAbsent(callback, wrapped) != null) {
-            throw new IllegalArgumentException(
-                    "callback instance already associated with advertising");
-        }
-
-        try {
-            gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                    periodicData, duration, maxExtendedAdvertisingEvents, wrapped,
-                    mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to start advertising set - ", e);
-            postStartSetFailure(handler, callback,
-                    AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-            return;
-        }
-    }
-
-    /**
-     * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link
-     * BluetoothLeAdvertiser#startAdvertisingSet}.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    public void stopAdvertisingSet(AdvertisingSetCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-
-        IAdvertisingSetCallback wrapped = mCallbackWrappers.remove(callback);
-        if (wrapped == null) {
-            return;
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-            gatt.stopAdvertisingSet(wrapped, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to stop advertising - ", e);
-        }
-    }
-
-    /**
-     * Cleans up advertisers. Should be called when bluetooth is down.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public void cleanup() {
-        mLegacyAdvertisers.clear();
-        mCallbackWrappers.clear();
-        mAdvertisingSets.clear();
-    }
-
-    // Compute the size of advertisement data or scan resp
-    @RequiresBluetoothAdvertisePermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
-    private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) {
-        if (data == null) return 0;
-        // Flags field is omitted if the advertising is not connectable.
-        int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0;
-        if (data.getServiceUuids() != null) {
-            int num16BitUuids = 0;
-            int num32BitUuids = 0;
-            int num128BitUuids = 0;
-            for (ParcelUuid uuid : data.getServiceUuids()) {
-                if (BluetoothUuid.is16BitUuid(uuid)) {
-                    ++num16BitUuids;
-                } else if (BluetoothUuid.is32BitUuid(uuid)) {
-                    ++num32BitUuids;
-                } else {
-                    ++num128BitUuids;
-                }
-            }
-            // 16 bit service uuids are grouped into one field when doing advertising.
-            if (num16BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
-            }
-            // 32 bit service uuids are grouped into one field when doing advertising.
-            if (num32BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
-            }
-            // 128 bit service uuids are grouped into one field when doing advertising.
-            if (num128BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD
-                        + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
-            }
-        }
-        if (data.getServiceSolicitationUuids() != null) {
-            int num16BitUuids = 0;
-            int num32BitUuids = 0;
-            int num128BitUuids = 0;
-            for (ParcelUuid uuid : data.getServiceSolicitationUuids()) {
-                if (BluetoothUuid.is16BitUuid(uuid)) {
-                    ++num16BitUuids;
-                } else if (BluetoothUuid.is32BitUuid(uuid)) {
-                    ++num32BitUuids;
-                } else {
-                    ++num128BitUuids;
-                }
-            }
-            // 16 bit service uuids are grouped into one field when doing advertising.
-            if (num16BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT;
-            }
-            // 32 bit service uuids are grouped into one field when doing advertising.
-            if (num32BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT;
-            }
-            // 128 bit service uuids are grouped into one field when doing advertising.
-            if (num128BitUuids != 0) {
-                size += OVERHEAD_BYTES_PER_FIELD
-                        + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
-            }
-        }
-        for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) {
-            size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes();
-        }
-        for (ParcelUuid uuid : data.getServiceData().keySet()) {
-            int uuidLen = BluetoothUuid.uuidToBytes(uuid).length;
-            size += OVERHEAD_BYTES_PER_FIELD + uuidLen
-                    + byteLength(data.getServiceData().get(uuid));
-        }
-        for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) {
-            size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH
-                    + byteLength(data.getManufacturerSpecificData().valueAt(i));
-        }
-        if (data.getIncludeTxPowerLevel()) {
-            size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
-        }
-        if (data.getIncludeDeviceName()) {
-            final int length = mBluetoothAdapter.getNameLengthForAdvertise();
-            if (length >= 0) {
-                size += OVERHEAD_BYTES_PER_FIELD + length;
-            }
-        }
-        return size;
-    }
-
-    private int byteLength(byte[] array) {
-        return array == null ? 0 : array.length;
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) {
-        return new IAdvertisingSetCallback.Stub() {
-            @Override
-            public void onAdvertisingSetStarted(int advertiserId, int txPower, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
-                            callback.onAdvertisingSetStarted(null, 0, status);
-                            mCallbackWrappers.remove(callback);
-                            return;
-                        }
-
-                        AdvertisingSet advertisingSet = new AdvertisingSet(
-                                advertiserId, mBluetoothManager, mAttributionSource);
-                        mAdvertisingSets.put(advertiserId, advertisingSet);
-                        callback.onAdvertisingSetStarted(advertisingSet, txPower, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onOwnAddressRead(int advertiserId, int addressType, String address) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onOwnAddressRead(advertisingSet, addressType, address);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingSetStopped(int advertiserId) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingSetStopped(advertisingSet);
-                        mAdvertisingSets.remove(advertiserId);
-                        mCallbackWrappers.remove(callback);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingEnabled(advertisingSet, enabled, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingDataSet(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingDataSet(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onScanResponseDataSet(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onScanResponseDataSet(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onAdvertisingParametersUpdated(advertisingSet, txPower, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onPeriodicAdvertisingDataSet(advertisingSet, status);
-                    }
-                });
-            }
-
-            @Override
-            public void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
-                        callback.onPeriodicAdvertisingEnabled(advertisingSet, enable, status);
-                    }
-                });
-            }
-        };
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback,
-            final int error) {
-        handler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onAdvertisingSetStarted(null, 0, error);
-            }
-        });
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postStartFailure(final AdvertiseCallback callback, final int error) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onStartFailure(error);
-            }
-        });
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postStartSuccess(final AdvertiseCallback callback,
-            final AdvertiseSettings settings) {
-        mHandler.post(new Runnable() {
-
-            @Override
-            public void run() {
-                callback.onStartSuccess(settings);
-            }
-        });
-    }
-}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
deleted file mode 100644
index 540e5a7..0000000
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresNoPermission;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.bluetooth.Attributable;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothGatt;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.WorkSource;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class provides methods to perform scan related operations for Bluetooth LE devices. An
- * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It
- * can also request different types of callbacks for delivering the result.
- * <p>
- * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
- * {@link BluetoothLeScanner}.
- *
- * @see ScanFilter
- */
-public final class BluetoothLeScanner {
-
-    private static final String TAG = "BluetoothLeScanner";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    /**
-     * Extra containing a list of ScanResults. It can have one or more results if there was no
-     * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this
-     * extra will not be available.
-     */
-    public static final String EXTRA_LIST_SCAN_RESULT =
-            "android.bluetooth.le.extra.LIST_SCAN_RESULT";
-
-    /**
-     * Optional extra indicating the error code, if any. The error code will be one of the
-     * SCAN_FAILED_* codes in {@link ScanCallback}.
-     */
-    public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
-
-    /**
-     * Optional extra indicating the callback type, which will be one of
-     * CALLBACK_TYPE_* constants in {@link ScanSettings}.
-     *
-     * @see ScanCallback#onScanResult(int, ScanResult)
-     */
-    public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
-
-    private final BluetoothAdapter mBluetoothAdapter;
-    private final IBluetoothManager mBluetoothManager;
-    private final AttributionSource mAttributionSource;
-
-    private final Handler mHandler;
-    private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
-
-    /**
-     * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
-     *
-     * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
-     * @param opPackageName The opPackageName of the context this object was created from
-     * @param featureId  The featureId of the context this object was created from
-     * @hide
-     */
-    public BluetoothLeScanner(BluetoothAdapter bluetoothAdapter) {
-        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
-        mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
-        mAttributionSource = mBluetoothAdapter.getAttributionSource();
-        mHandler = new Handler(Looper.getMainLooper());
-        mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
-    }
-
-    /**
-     * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
-     * delivered through {@code callback}. For unfiltered scans, scanning is stopped on screen
-     * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use
-     * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}.
-     * <p>
-     * An app must have
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
-     * in order to get results. An App targeting Android Q or later must have
-     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get results.
-     *
-     * @param callback Callback used to deliver scan results.
-     * @throws IllegalArgumentException If {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void startScan(final ScanCallback callback) {
-        startScan(null, new ScanSettings.Builder().build(), callback);
-    }
-
-    /**
-     * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
-     * For unfiltered scans, scanning is stopped on screen off to save power. Scanning is
-     * resumed when screen is turned on again. To avoid this, do filetered scanning by
-     * using proper {@link ScanFilter}.
-     * <p>
-     * An app must have
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
-     * in order to get results. An App targeting Android Q or later must have
-     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get results.
-     *
-     * @param filters {@link ScanFilter}s for finding exact BLE devices.
-     * @param settings Settings for the scan.
-     * @param callback Callback used to deliver scan results.
-     * @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void startScan(List<ScanFilter> filters, ScanSettings settings,
-            final ScanCallback callback) {
-        startScan(filters, settings, null, callback, /*callbackIntent=*/ null);
-    }
-
-    /**
-     * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via
-     * the PendingIntent. Use this method of scanning if your process is not always running and it
-     * should be started when scan results are available.
-     * <p>
-     * An app must have
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
-     * in order to get results. An App targeting Android Q or later must have
-     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
-     * in order to get results.
-     * <p>
-     * When the PendingIntent is delivered, the Intent passed to the receiver or activity
-     * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE},
-     * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of
-     * the scan.
-     *
-     * @param filters Optional list of ScanFilters for finding exact BLE devices.
-     * @param settings Optional settings for the scan.
-     * @param callbackIntent The PendingIntent to deliver the result to.
-     * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request
-     * could not be sent.
-     * @see #stopScan(PendingIntent)
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings,
-            @NonNull PendingIntent callbackIntent) {
-        return startScan(filters,
-                settings != null ? settings : new ScanSettings.Builder().build(),
-                null, null, callbackIntent);
-    }
-
-    /**
-     * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to
-     * specify on behalf of which application(s) the work is being done.
-     *
-     * @param workSource {@link WorkSource} identifying the application(s) for which to blame for
-     * the scan.
-     * @param callback Callback used to deliver scan results.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.UPDATE_DEVICE_STATS
-    })
-    public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) {
-        startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback);
-    }
-
-    /**
-     * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but
-     * allows the caller to specify on behalf of which application(s) the work is being done.
-     *
-     * @param filters {@link ScanFilter}s for finding exact BLE devices.
-     * @param settings Settings for the scan.
-     * @param workSource {@link WorkSource} identifying the application(s) for which to blame for
-     * the scan.
-     * @param callback Callback used to deliver scan results.
-     * @hide
-     */
-    @SystemApi
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_SCAN,
-            android.Manifest.permission.UPDATE_DEVICE_STATS
-    })
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
-            final WorkSource workSource, final ScanCallback callback) {
-        startScan(filters, settings, workSource, callback, null);
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    private int startScan(List<ScanFilter> filters, ScanSettings settings,
-            final WorkSource workSource, final ScanCallback callback,
-            final PendingIntent callbackIntent) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        if (callback == null && callbackIntent == null) {
-            throw new IllegalArgumentException("callback is null");
-        }
-        if (settings == null) {
-            throw new IllegalArgumentException("settings is null");
-        }
-        synchronized (mLeScanClients) {
-            if (callback != null && mLeScanClients.containsKey(callback)) {
-                return postCallbackErrorOrReturn(callback,
-                            ScanCallback.SCAN_FAILED_ALREADY_STARTED);
-            }
-            IBluetoothGatt gatt;
-            try {
-                gatt = mBluetoothManager.getBluetoothGatt();
-            } catch (RemoteException e) {
-                gatt = null;
-            }
-            if (gatt == null) {
-                return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
-            }
-            if (!isSettingsConfigAllowedForScan(settings)) {
-                return postCallbackErrorOrReturn(callback,
-                        ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
-            }
-            if (!isHardwareResourcesAvailableForScan(settings)) {
-                return postCallbackErrorOrReturn(callback,
-                        ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
-            }
-            if (!isSettingsAndFilterComboAllowed(settings, filters)) {
-                return postCallbackErrorOrReturn(callback,
-                        ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
-            }
-            if (callback != null) {
-                BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
-                        settings, workSource, callback);
-                wrapper.startRegistration();
-            } else {
-                try {
-                    gatt.startScanForIntent(callbackIntent, settings, filters,
-                            mAttributionSource);
-                } catch (RemoteException e) {
-                    return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
-                }
-            }
-        }
-        return ScanCallback.NO_ERROR;
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE scan.
-     *
-     * @param callback
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void stopScan(ScanCallback callback) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        synchronized (mLeScanClients) {
-            BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback);
-            if (wrapper == null) {
-                if (DBG) Log.d(TAG, "could not find callback wrapper");
-                return;
-            }
-            wrapper.stopLeScan();
-        }
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the
-     * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop
-     * scan may have no effect.
-     *
-     * @param callbackIntent The PendingIntent that was used to start the scan.
-     * @see #startScan(List, ScanSettings, PendingIntent)
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void stopScan(PendingIntent callbackIntent) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-            gatt.stopScanForIntent(callbackIntent, mAttributionSource);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
-     * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
-     * will be delivered through the {@code callback}.
-     *
-     * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
-     * used to start scan.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void flushPendingScanResults(ScanCallback callback) {
-        BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null!");
-        }
-        synchronized (mLeScanClients) {
-            BleScanCallbackWrapper wrapper = mLeScanClients.get(callback);
-            if (wrapper == null) {
-                return;
-            }
-            wrapper.flushPendingBatchResults();
-        }
-    }
-
-    /**
-     * Start truncated scan.
-     *
-     * @deprecated this is not used anywhere
-     *
-     * @hide
-     */
-    @Deprecated
-    @SystemApi
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings,
-            final ScanCallback callback) {
-        int filterSize = truncatedFilters.size();
-        List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
-        for (TruncatedFilter filter : truncatedFilters) {
-            scanFilters.add(filter.getFilter());
-        }
-        startScan(scanFilters, settings, null, callback, null);
-    }
-
-    /**
-     * Cleans up scan clients. Should be called when bluetooth is down.
-     *
-     * @hide
-     */
-    @RequiresNoPermission
-    public void cleanup() {
-        mLeScanClients.clear();
-    }
-
-    /**
-     * Bluetooth GATT interface callbacks
-     */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private class BleScanCallbackWrapper extends IScannerCallback.Stub {
-        private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
-
-        private final ScanCallback mScanCallback;
-        private final List<ScanFilter> mFilters;
-        private final WorkSource mWorkSource;
-        private ScanSettings mSettings;
-        private IBluetoothGatt mBluetoothGatt;
-
-        // mLeHandle 0: not registered
-        // -2: registration failed because app is scanning to frequently
-        // -1: scan stopped or registration failed
-        // > 0: registered and scan started
-        private int mScannerId;
-
-        public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
-                List<ScanFilter> filters, ScanSettings settings,
-                WorkSource workSource, ScanCallback scanCallback) {
-            mBluetoothGatt = bluetoothGatt;
-            mFilters = filters;
-            mSettings = settings;
-            mWorkSource = workSource;
-            mScanCallback = scanCallback;
-            mScannerId = 0;
-        }
-
-        public void startRegistration() {
-            synchronized (this) {
-                // Scan stopped.
-                if (mScannerId == -1 || mScannerId == -2) return;
-                try {
-                    mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource);
-                    wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
-                } catch (InterruptedException | RemoteException e) {
-                    Log.e(TAG, "application registeration exception", e);
-                    postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
-                }
-                if (mScannerId > 0) {
-                    mLeScanClients.put(mScanCallback, this);
-                } else {
-                    // Registration timed out or got exception, reset RscannerId to -1 so no
-                    // subsequent operations can proceed.
-                    if (mScannerId == 0) mScannerId = -1;
-
-                    // If scanning too frequently, don't report anything to the app.
-                    if (mScannerId == -2) return;
-
-                    postCallbackError(mScanCallback,
-                            ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
-                }
-            }
-        }
-
-        @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-        public void stopLeScan() {
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
-                    return;
-                }
-                try {
-                    mBluetoothGatt.stopScan(mScannerId, mAttributionSource);
-                    mBluetoothGatt.unregisterScanner(mScannerId, mAttributionSource);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to stop scan and unregister", e);
-                }
-                mScannerId = -1;
-            }
-        }
-
-        @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-        void flushPendingBatchResults() {
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mScannerId);
-                    return;
-                }
-                try {
-                    mBluetoothGatt.flushPendingBatchResults(mScannerId, mAttributionSource);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to get pending scan results", e);
-                }
-            }
-        }
-
-        /**
-         * Application interface registered - app is ready to go
-         */
-        @Override
-        public void onScannerRegistered(int status, int scannerId) {
-            Log.d(TAG, "onScannerRegistered() - status=" + status
-                    + " scannerId=" + scannerId + " mScannerId=" + mScannerId);
-            synchronized (this) {
-                if (status == BluetoothGatt.GATT_SUCCESS) {
-                    try {
-                        if (mScannerId == -1) {
-                            // Registration succeeds after timeout, unregister scanner.
-                            mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource);
-                        } else {
-                            mScannerId = scannerId;
-                            mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
-                                    mAttributionSource);
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "fail to start le scan: " + e);
-                        mScannerId = -1;
-                    }
-                } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) {
-                    // applicaiton was scanning too frequently
-                    mScannerId = -2;
-                } else {
-                    // registration failed
-                    mScannerId = -1;
-                }
-                notifyAll();
-            }
-        }
-
-        /**
-         * Callback reporting an LE scan result.
-         *
-         * @hide
-         */
-        @Override
-        public void onScanResult(final ScanResult scanResult) {
-            Attributable.setAttributionSource(scanResult, mAttributionSource);
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId);
-            }
-            if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
-
-            // Check null in case the scan has been stopped
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Ignoring result as scan stopped.");
-                    }
-                    return;
-                };
-            }
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "onScanResult() - handler run");
-                    }
-                    mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
-                }
-            });
-        }
-
-        @Override
-        public void onBatchScanResults(final List<ScanResult> results) {
-            Attributable.setAttributionSource(results, mAttributionSource);
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mScanCallback.onBatchScanResults(results);
-                }
-            });
-        }
-
-        @Override
-        public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) {
-            Attributable.setAttributionSource(scanResult, mAttributionSource);
-            if (VDBG) {
-                Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString());
-            }
-
-            // Check null in case the scan has been stopped
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    return;
-                }
-            }
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    if (onFound) {
-                        mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH,
-                                scanResult);
-                    } else {
-                        mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST,
-                                scanResult);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onScanManagerErrorCallback(final int errorCode) {
-            if (VDBG) {
-                Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
-            }
-            synchronized (this) {
-                if (mScannerId <= 0) {
-                    return;
-                }
-            }
-            postCallbackError(mScanCallback, errorCode);
-        }
-    }
-
-    private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) {
-        if (callback == null) {
-            return errorCode;
-        } else {
-            postCallbackError(callback, errorCode);
-            return ScanCallback.NO_ERROR;
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private void postCallbackError(final ScanCallback callback, final int errorCode) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onScanFailed(errorCode);
-            }
-        });
-    }
-
-    private boolean isSettingsConfigAllowedForScan(ScanSettings settings) {
-        if (mBluetoothAdapter.isOffloadedFilteringSupported()) {
-            return true;
-        }
-        final int callbackType = settings.getCallbackType();
-        // Only support regular scan if no offloaded filter support.
-        if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
-                && settings.getReportDelayMillis() == 0) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isSettingsAndFilterComboAllowed(ScanSettings settings,
-            List<ScanFilter> filterList) {
-        final int callbackType = settings.getCallbackType();
-        // If onlost/onfound is requested, a non-empty filter is expected
-        if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
-                | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
-            if (filterList == null) {
-                return false;
-            }
-            for (ScanFilter filter : filterList) {
-                if (filter.isAllFieldsEmpty()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
-        final int callbackType = settings.getCallbackType();
-        if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
-                || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
-            // For onlost/onfound, we required hw support be available
-            return (mBluetoothAdapter.isOffloadedFilteringSupported()
-                    && mBluetoothAdapter.isHardwareTrackingFiltersAvailable());
-        }
-        return true;
-    }
-}
diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java
deleted file mode 100644
index ed50b09..0000000
--- a/core/java/android/bluetooth/le/BluetoothLeUtils.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.util.SparseArray;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * Helper class for Bluetooth LE utils.
- *
- * @hide
- */
-public class BluetoothLeUtils {
-
-    /**
-     * Returns a string composed from a {@link SparseArray}.
-     */
-    static String toString(SparseArray<byte[]> array) {
-        if (array == null) {
-            return "null";
-        }
-        if (array.size() == 0) {
-            return "{}";
-        }
-        StringBuilder buffer = new StringBuilder();
-        buffer.append('{');
-        for (int i = 0; i < array.size(); ++i) {
-            buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i)));
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-    /**
-     * Returns a string composed from a {@link Map}.
-     */
-    static <T> String toString(Map<T, byte[]> map) {
-        if (map == null) {
-            return "null";
-        }
-        if (map.isEmpty()) {
-            return "{}";
-        }
-        StringBuilder buffer = new StringBuilder();
-        buffer.append('{');
-        Iterator<Map.Entry<T, byte[]>> it = map.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry<T, byte[]> entry = it.next();
-            Object key = entry.getKey();
-            buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
-            if (it.hasNext()) {
-                buffer.append(", ");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-    /**
-     * Check whether two {@link SparseArray} equal.
-     */
-    static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) {
-        if (array == otherArray) {
-            return true;
-        }
-        if (array == null || otherArray == null) {
-            return false;
-        }
-        if (array.size() != otherArray.size()) {
-            return false;
-        }
-
-        // Keys are guaranteed in ascending order when indices are in ascending order.
-        for (int i = 0; i < array.size(); ++i) {
-            if (array.keyAt(i) != otherArray.keyAt(i)
-                    || !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Check whether two {@link Map} equal.
-     */
-    static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) {
-        if (map == otherMap) {
-            return true;
-        }
-        if (map == null || otherMap == null) {
-            return false;
-        }
-        if (map.size() != otherMap.size()) {
-            return false;
-        }
-        Set<T> keys = map.keySet();
-        if (!keys.equals(otherMap.keySet())) {
-            return false;
-        }
-        for (T key : keys) {
-            if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Ensure Bluetooth is turned on.
-     *
-     * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not {@link
-     * BluetoothAdapter#STATE_ON}.
-     */
-    static void checkAdapterStateOn(BluetoothAdapter adapter) {
-        if (adapter == null || !adapter.isLeEnabled()) {
-            throw new IllegalStateException("BT Adapter is not turned ON");
-        }
-    }
-
-    /**
-     * Compares two UUIDs with a UUID mask.
-     *
-     * @param data first {@link #UUID} to compare.
-     * @param uuid second {@link #UUID} to compare.
-     * @param mask mask {@link #UUID}.
-     * @return true if both UUIDs are equals when masked, false otherwise.
-     */
-    static boolean maskedEquals(UUID data, UUID uuid, UUID mask) {
-        if (mask == null) {
-            return Objects.equals(data, uuid);
-        }
-        return (data.getLeastSignificantBits() & mask.getLeastSignificantBits())
-                == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
-                && (data.getMostSignificantBits() & mask.getMostSignificantBits())
-                == (uuid.getMostSignificantBits() & mask.getMostSignificantBits());
-    }
-}
diff --git a/core/java/android/bluetooth/le/OWNERS b/core/java/android/bluetooth/le/OWNERS
deleted file mode 100644
index 3523ee0..0000000
--- a/core/java/android/bluetooth/le/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 27441
-
-zachoverflow@google.com
-siyuanh@google.com
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
deleted file mode 100644
index 14ac911..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.bluetooth.BluetoothDevice;
-
-/**
- * Bluetooth LE periodic advertising callbacks, used to deliver periodic
- * advertising operation status.
- *
- * @hide
- * @see PeriodicAdvertisingManager#createSync
- */
-public abstract class PeriodicAdvertisingCallback {
-
-    /**
-     * The requested operation was successful.
-     *
-     * @hide
-     */
-    public static final int SYNC_SUCCESS = 0;
-
-    /**
-     * Sync failed to be established because remote device did not respond.
-     */
-    public static final int SYNC_NO_RESPONSE = 1;
-
-    /**
-     * Sync failed to be established because controller can't support more syncs.
-     */
-    public static final int SYNC_NO_RESOURCES = 2;
-
-
-    /**
-     * Callback when synchronization was established.
-     *
-     * @param syncHandle handle used to identify this synchronization.
-     * @param device remote device.
-     * @param advertisingSid synchronized advertising set id.
-     * @param skip The number of periodic advertising packets that can be skipped after a successful
-     * receive in force. @see PeriodicAdvertisingManager#createSync
-     * @param timeout Synchronization timeout for the periodic advertising in force. One unit is
-     * 10ms. @see PeriodicAdvertisingManager#createSync
-     * @param timeout
-     * @param status operation status.
-     */
-    public void onSyncEstablished(int syncHandle, BluetoothDevice device,
-            int advertisingSid, int skip, int timeout,
-            int status) {
-    }
-
-    /**
-     * Callback when periodic advertising report is received.
-     *
-     * @param report periodic advertising report.
-     */
-    public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
-    }
-
-    /**
-     * Callback when periodic advertising synchronization was lost.
-     *
-     * @param syncHandle handle used to identify this synchronization.
-     */
-    public void onSyncLost(int syncHandle) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
deleted file mode 100644
index bbd3117..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.bluetooth.Attributable;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
-import android.bluetooth.annotations.RequiresBluetoothScanPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.AttributionSource;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This class provides methods to perform periodic advertising related
- * operations. An application can register for periodic advertisements using
- * {@link PeriodicAdvertisingManager#registerSync}.
- * <p>
- * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an
- * instance of {@link PeriodicAdvertisingManager}.
- *
- * @hide
- */
-public final class PeriodicAdvertisingManager {
-
-    private static final String TAG = "PeriodicAdvertisingManager";
-
-    private static final int SKIP_MIN = 0;
-    private static final int SKIP_MAX = 499;
-    private static final int TIMEOUT_MIN = 10;
-    private static final int TIMEOUT_MAX = 16384;
-
-    private static final int SYNC_STARTING = -1;
-
-    private final BluetoothAdapter mBluetoothAdapter;
-    private final IBluetoothManager mBluetoothManager;
-    private final AttributionSource mAttributionSource;
-
-    /* maps callback, to callback wrapper and sync handle */
-    Map<PeriodicAdvertisingCallback,
-            IPeriodicAdvertisingCallback /* callbackWrapper */> mCallbackWrappers;
-
-    /**
-     * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
-     *
-     * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
-     * @hide
-     */
-    public PeriodicAdvertisingManager(BluetoothAdapter bluetoothAdapter) {
-        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
-        mBluetoothManager = mBluetoothAdapter.getBluetoothManager();
-        mAttributionSource = mBluetoothAdapter.getAttributionSource();
-        mCallbackWrappers = new IdentityHashMap<>();
-    }
-
-    /**
-     * Synchronize with periodic advertising pointed to by the {@code scanResult}.
-     * The {@code scanResult} used must contain a valid advertisingSid. First
-     * call to registerSync will use the {@code skip} and {@code timeout} provided.
-     * Subsequent calls from other apps, trying to sync with same set will reuse
-     * existing sync, thus {@code skip} and {@code timeout} values will not take
-     * effect. The values in effect will be returned in
-     * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
-     *
-     * @param scanResult Scan result containing advertisingSid.
-     * @param skip The number of periodic advertising packets that can be skipped after a successful
-     * receive. Must be between 0 and 499.
-     * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must
-     * be between 10 (100ms) and 16384 (163.84s).
-     * @param callback Callback used to deliver all operations status.
-     * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
-     * {@code timeout} is invalid or {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void registerSync(ScanResult scanResult, int skip, int timeout,
-            PeriodicAdvertisingCallback callback) {
-        registerSync(scanResult, skip, timeout, callback, null);
-    }
-
-    /**
-     * Synchronize with periodic advertising pointed to by the {@code scanResult}.
-     * The {@code scanResult} used must contain a valid advertisingSid. First
-     * call to registerSync will use the {@code skip} and {@code timeout} provided.
-     * Subsequent calls from other apps, trying to sync with same set will reuse
-     * existing sync, thus {@code skip} and {@code timeout} values will not take
-     * effect. The values in effect will be returned in
-     * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
-     *
-     * @param scanResult Scan result containing advertisingSid.
-     * @param skip The number of periodic advertising packets that can be skipped after a successful
-     * receive. Must be between 0 and 499.
-     * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must
-     * be between 10 (100ms) and 16384 (163.84s).
-     * @param callback Callback used to deliver all operations status.
-     * @param handler thread upon which the callbacks will be invoked.
-     * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
-     * {@code timeout} is invalid or {@code callback} is null.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresBluetoothLocationPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void registerSync(ScanResult scanResult, int skip, int timeout,
-            PeriodicAdvertisingCallback callback, Handler handler) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback can't be null");
-        }
-
-        if (scanResult == null) {
-            throw new IllegalArgumentException("scanResult can't be null");
-        }
-
-        if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) {
-            throw new IllegalArgumentException("scanResult must contain a valid sid");
-        }
-
-        if (skip < SKIP_MIN || skip > SKIP_MAX) {
-            throw new IllegalArgumentException(
-                    "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
-        }
-
-        if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) {
-            throw new IllegalArgumentException(
-                    "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-            callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(),
-                    skip, timeout,
-                    PeriodicAdvertisingCallback.SYNC_NO_RESOURCES);
-            return;
-        }
-
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-
-        IPeriodicAdvertisingCallback wrapped = wrap(callback, handler);
-        mCallbackWrappers.put(callback, wrapped);
-
-        try {
-            gatt.registerSync(
-                    scanResult, skip, timeout, wrapped, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to register sync - ", e);
-            return;
-        }
-    }
-
-    /**
-     * Cancel pending attempt to create sync, or terminate existing sync.
-     *
-     * @param callback Callback used to deliver all operations status.
-     * @throws IllegalArgumentException if {@code callback} is null, or not a properly registered
-     * callback.
-     */
-    @RequiresLegacyBluetoothAdminPermission
-    @RequiresBluetoothScanPermission
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
-    public void unregisterSync(PeriodicAdvertisingCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback can't be null");
-        }
-
-        IBluetoothGatt gatt;
-        try {
-            gatt = mBluetoothManager.getBluetoothGatt();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-            return;
-        }
-
-        IPeriodicAdvertisingCallback wrapper = mCallbackWrappers.remove(callback);
-        if (wrapper == null) {
-            throw new IllegalArgumentException("callback was not properly registered");
-        }
-
-        try {
-            gatt.unregisterSync(wrapper, mAttributionSource);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to cancel sync creation - ", e);
-            return;
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback,
-            Handler handler) {
-        return new IPeriodicAdvertisingCallback.Stub() {
-            public void onSyncEstablished(int syncHandle, BluetoothDevice device,
-                    int advertisingSid, int skip, int timeout, int status) {
-                Attributable.setAttributionSource(device, mAttributionSource);
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onSyncEstablished(syncHandle, device, advertisingSid, skip,
-                                timeout,
-                                status);
-
-                        if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) {
-                            // App can still unregister the sync until notified it failed. Remove
-                            // callback
-                            // after app was notifed.
-                            mCallbackWrappers.remove(callback);
-                        }
-                    }
-                });
-            }
-
-            public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onPeriodicAdvertisingReport(report);
-                    }
-                });
-            }
-
-            public void onSyncLost(int syncHandle) {
-                handler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onSyncLost(syncHandle);
-                        // App can still unregister the sync until notified it's lost.
-                        // Remove callback after app was notifed.
-                        mCallbackWrappers.remove(callback);
-                    }
-                });
-            }
-        };
-    }
-}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
deleted file mode 100644
index 4e64dbe..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic
- * advertising preferences for each Bluetooth LE advertising set. Use {@link
- * PeriodicAdvertisingParameters.Builder} to create an instance of this class.
- */
-public final class PeriodicAdvertisingParameters implements Parcelable {
-
-    private static final int INTERVAL_MIN = 80;
-    private static final int INTERVAL_MAX = 65519;
-
-    private final boolean mIncludeTxPower;
-    private final int mInterval;
-
-    private PeriodicAdvertisingParameters(boolean includeTxPower, int interval) {
-        mIncludeTxPower = includeTxPower;
-        mInterval = interval;
-    }
-
-    private PeriodicAdvertisingParameters(Parcel in) {
-        mIncludeTxPower = in.readInt() != 0;
-        mInterval = in.readInt();
-    }
-
-    /**
-     * Returns whether the TX Power will be included.
-     */
-    public boolean getIncludeTxPower() {
-        return mIncludeTxPower;
-    }
-
-    /**
-     * Returns the periodic advertising interval, in 1.25ms unit.
-     * Valid values are from 80 (100ms) to 65519 (81.89875s).
-     */
-    public int getInterval() {
-        return mInterval;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mIncludeTxPower ? 1 : 0);
-        dest.writeInt(mInterval);
-    }
-
-    public static final Parcelable
-            .Creator<PeriodicAdvertisingParameters> CREATOR =
-            new Creator<PeriodicAdvertisingParameters>() {
-                @Override
-                public PeriodicAdvertisingParameters[] newArray(int size) {
-                    return new PeriodicAdvertisingParameters[size];
-                }
-
-                @Override
-                public PeriodicAdvertisingParameters createFromParcel(Parcel in) {
-                    return new PeriodicAdvertisingParameters(in);
-                }
-            };
-
-    public static final class Builder {
-        private boolean mIncludeTxPower = false;
-        private int mInterval = INTERVAL_MAX;
-
-        /**
-         * Whether the transmission power level should be included in the periodic
-         * packet.
-         */
-        public Builder setIncludeTxPower(boolean includeTxPower) {
-            mIncludeTxPower = includeTxPower;
-            return this;
-        }
-
-        /**
-         * Set advertising interval for periodic advertising, in 1.25ms unit.
-         * Valid values are from 80 (100ms) to 65519 (81.89875s).
-         * Value from range [interval, interval+20ms] will be picked as the actual value.
-         *
-         * @throws IllegalArgumentException If the interval is invalid.
-         */
-        public Builder setInterval(int interval) {
-            if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
-                throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN
-                        + "-" + INTERVAL_MAX + ")");
-            }
-            mInterval = interval;
-            return this;
-        }
-
-        /**
-         * Build the {@link AdvertisingSetParameters} object.
-         */
-        public PeriodicAdvertisingParameters build() {
-            return new PeriodicAdvertisingParameters(mIncludeTxPower, mInterval);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
deleted file mode 100644
index 54b953c..0000000
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth.le;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising.
- *
- * @hide
- */
-public final class PeriodicAdvertisingReport implements Parcelable {
-
-    /**
-     * The data returned is complete
-     */
-    public static final int DATA_COMPLETE = 0;
-
-    /**
-     * The data returned is incomplete. The controller was unsuccessfull to
-     * receive all chained packets, returning only partial data.
-     */
-    public static final int DATA_INCOMPLETE_TRUNCATED = 2;
-
-    private int mSyncHandle;
-    private int mTxPower;
-    private int mRssi;
-    private int mDataStatus;
-
-    // periodic advertising data.
-    @Nullable
-    private ScanRecord mData;
-
-    // Device timestamp when the result was last seen.
-    private long mTimestampNanos;
-
-    /**
-     * Constructor of periodic advertising result.
-     */
-    public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi,
-            int dataStatus, ScanRecord data) {
-        mSyncHandle = syncHandle;
-        mTxPower = txPower;
-        mRssi = rssi;
-        mDataStatus = dataStatus;
-        mData = data;
-    }
-
-    private PeriodicAdvertisingReport(Parcel in) {
-        readFromParcel(in);
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSyncHandle);
-        dest.writeInt(mTxPower);
-        dest.writeInt(mRssi);
-        dest.writeInt(mDataStatus);
-        if (mData != null) {
-            dest.writeInt(1);
-            dest.writeByteArray(mData.getBytes());
-        } else {
-            dest.writeInt(0);
-        }
-    }
-
-    private void readFromParcel(Parcel in) {
-        mSyncHandle = in.readInt();
-        mTxPower = in.readInt();
-        mRssi = in.readInt();
-        mDataStatus = in.readInt();
-        if (in.readInt() == 1) {
-            mData = ScanRecord.parseFromBytes(in.createByteArray());
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Returns the synchronization handle.
-     */
-    public int getSyncHandle() {
-        return mSyncHandle;
-    }
-
-    /**
-     * Returns the transmit power in dBm. The valid range is [-127, 126]. Value
-     * of 127 means information was not available.
-     */
-    public int getTxPower() {
-        return mTxPower;
-    }
-
-    /**
-     * Returns the received signal strength in dBm. The valid range is [-127, 20].
-     */
-    public int getRssi() {
-        return mRssi;
-    }
-
-    /**
-     * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE}
-     * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}.
-     */
-    public int getDataStatus() {
-        return mDataStatus;
-    }
-
-    /**
-     * Returns the data contained in this periodic advertising report.
-     */
-    @Nullable
-    public ScanRecord getData() {
-        return mData;
-    }
-
-    /**
-     * Returns timestamp since boot when the scan record was observed.
-     */
-    public long getTimestampNanos() {
-        return mTimestampNanos;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mSyncHandle, mTxPower, mRssi, mDataStatus, mData, mTimestampNanos);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj;
-        return (mSyncHandle == other.mSyncHandle)
-                && (mTxPower == other.mTxPower)
-                && (mRssi == other.mRssi)
-                && (mDataStatus == other.mDataStatus)
-                && Objects.equals(mData, other.mData)
-                && (mTimestampNanos == other.mTimestampNanos);
-    }
-
-    @Override
-    public String toString() {
-        return "PeriodicAdvertisingReport{syncHandle=" + mSyncHandle
-                + ", txPower=" + mTxPower + ", rssi=" + mRssi + ", dataStatus=" + mDataStatus
-                + ", data=" + Objects.toString(mData) + ", timestampNanos=" + mTimestampNanos + '}';
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<PeriodicAdvertisingReport> CREATOR =
-            new Creator<PeriodicAdvertisingReport>() {
-                @Override
-                public PeriodicAdvertisingReport createFromParcel(Parcel source) {
-                    return new PeriodicAdvertisingReport(source);
-                }
-
-                @Override
-                public PeriodicAdvertisingReport[] newArray(int size) {
-                    return new PeriodicAdvertisingReport[size];
-                }
-            };
-}
diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
deleted file mode 100644
index f650489..0000000
--- a/core/java/android/bluetooth/le/ResultStorageDescriptor.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Describes the way to store scan result.
- *
- * @deprecated this is not used anywhere
- *
- * @hide
- */
-@Deprecated
-@SystemApi
-public final class ResultStorageDescriptor implements Parcelable {
-    private int mType;
-    private int mOffset;
-    private int mLength;
-
-    public int getType() {
-        return mType;
-    }
-
-    public int getOffset() {
-        return mOffset;
-    }
-
-    public int getLength() {
-        return mLength;
-    }
-
-    /**
-     * Constructor of {@link ResultStorageDescriptor}
-     *
-     * @param type Type of the data.
-     * @param offset Offset from start of the advertise packet payload.
-     * @param length Byte length of the data
-     */
-    public ResultStorageDescriptor(int type, int offset, int length) {
-        mType = type;
-        mOffset = offset;
-        mLength = length;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mType);
-        dest.writeInt(mOffset);
-        dest.writeInt(mLength);
-    }
-
-    private ResultStorageDescriptor(Parcel in) {
-        ReadFromParcel(in);
-    }
-
-    private void ReadFromParcel(Parcel in) {
-        mType = in.readInt();
-        mOffset = in.readInt();
-        mLength = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ResultStorageDescriptor> CREATOR =
-            new Creator<ResultStorageDescriptor>() {
-        @Override
-        public ResultStorageDescriptor createFromParcel(Parcel source) {
-            return new ResultStorageDescriptor(source);
-        }
-
-        @Override
-        public ResultStorageDescriptor[] newArray(int size) {
-            return new ResultStorageDescriptor[size];
-        }
-    };
-}
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
deleted file mode 100644
index 53d9310..0000000
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import java.util.List;
-
-/**
- * Bluetooth LE scan callbacks. Scan results are reported using these callbacks.
- *
- * @see BluetoothLeScanner#startScan
- */
-public abstract class ScanCallback {
-    /**
-     * Fails to start scan as BLE scan with the same settings is already started by the app.
-     */
-    public static final int SCAN_FAILED_ALREADY_STARTED = 1;
-
-    /**
-     * Fails to start scan as app cannot be registered.
-     */
-    public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2;
-
-    /**
-     * Fails to start scan due an internal error
-     */
-    public static final int SCAN_FAILED_INTERNAL_ERROR = 3;
-
-    /**
-     * Fails to start power optimized scan as this feature is not supported.
-     */
-    public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4;
-
-    /**
-     * Fails to start scan as it is out of hardware resources.
-     *
-     * @hide
-     */
-    public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
-
-    /**
-     * Fails to start scan as application tries to scan too frequently.
-     * @hide
-     */
-    public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6;
-
-    static final int NO_ERROR = 0;
-
-    /**
-     * Callback when a BLE advertisement has been found.
-     *
-     * @param callbackType Determines how this callback was triggered. Could be one of {@link
-     * ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
-     * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST}
-     * @param result A Bluetooth LE scan result.
-     */
-    public void onScanResult(int callbackType, ScanResult result) {
-    }
-
-    /**
-     * Callback when batch results are delivered.
-     *
-     * @param results List of scan results that are previously scanned.
-     */
-    public void onBatchScanResults(List<ScanResult> results) {
-    }
-
-    /**
-     * Callback when scan could not be started.
-     *
-     * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure.
-     */
-    public void onScanFailed(int errorCode) {
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
deleted file mode 100644
index b059193..0000000
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothDevice.AddressType;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to
- * restrict scan results to only those that are of interest to them.
- * <p>
- * Current filtering on the following fields are supported:
- * <li>Service UUIDs which identify the bluetooth gatt services running on the device.
- * <li>Name of remote Bluetooth LE device.
- * <li>Mac address of the remote device.
- * <li>Service data which is the data associated with a service.
- * <li>Manufacturer specific data which is the data associated with a particular manufacturer.
- *
- * @see ScanResult
- * @see BluetoothLeScanner
- */
-public final class ScanFilter implements Parcelable {
-
-    @Nullable
-    private final String mDeviceName;
-
-    @Nullable
-    private final String mDeviceAddress;
-
-    private final @AddressType int mAddressType;
-
-    @Nullable
-    private final byte[] mIrk;
-
-    @Nullable
-    private final ParcelUuid mServiceUuid;
-    @Nullable
-    private final ParcelUuid mServiceUuidMask;
-
-    @Nullable
-    private final ParcelUuid mServiceSolicitationUuid;
-    @Nullable
-    private final ParcelUuid mServiceSolicitationUuidMask;
-
-    @Nullable
-    private final ParcelUuid mServiceDataUuid;
-    @Nullable
-    private final byte[] mServiceData;
-    @Nullable
-    private final byte[] mServiceDataMask;
-
-    private final int mManufacturerId;
-    @Nullable
-    private final byte[] mManufacturerData;
-    @Nullable
-    private final byte[] mManufacturerDataMask;
-
-    /** @hide */
-    public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
-
-    private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
-            ParcelUuid uuidMask, ParcelUuid solicitationUuid,
-            ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
-            byte[] serviceData, byte[] serviceDataMask,
-            int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
-            @AddressType int addressType, @Nullable byte[] irk) {
-        mDeviceName = name;
-        mServiceUuid = uuid;
-        mServiceUuidMask = uuidMask;
-        mServiceSolicitationUuid = solicitationUuid;
-        mServiceSolicitationUuidMask = solicitationUuidMask;
-        mDeviceAddress = deviceAddress;
-        mServiceDataUuid = serviceDataUuid;
-        mServiceData = serviceData;
-        mServiceDataMask = serviceDataMask;
-        mManufacturerId = manufacturerId;
-        mManufacturerData = manufacturerData;
-        mManufacturerDataMask = manufacturerDataMask;
-        mAddressType = addressType;
-        mIrk = irk;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mDeviceName == null ? 0 : 1);
-        if (mDeviceName != null) {
-            dest.writeString(mDeviceName);
-        }
-        dest.writeInt(mDeviceAddress == null ? 0 : 1);
-        if (mDeviceAddress != null) {
-            dest.writeString(mDeviceAddress);
-        }
-        dest.writeInt(mServiceUuid == null ? 0 : 1);
-        if (mServiceUuid != null) {
-            dest.writeParcelable(mServiceUuid, flags);
-            dest.writeInt(mServiceUuidMask == null ? 0 : 1);
-            if (mServiceUuidMask != null) {
-                dest.writeParcelable(mServiceUuidMask, flags);
-            }
-        }
-        dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1);
-        if (mServiceSolicitationUuid != null) {
-            dest.writeParcelable(mServiceSolicitationUuid, flags);
-            dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1);
-            if (mServiceSolicitationUuidMask != null) {
-                dest.writeParcelable(mServiceSolicitationUuidMask, flags);
-            }
-        }
-        dest.writeInt(mServiceDataUuid == null ? 0 : 1);
-        if (mServiceDataUuid != null) {
-            dest.writeParcelable(mServiceDataUuid, flags);
-            dest.writeInt(mServiceData == null ? 0 : 1);
-            if (mServiceData != null) {
-                dest.writeInt(mServiceData.length);
-                dest.writeByteArray(mServiceData);
-
-                dest.writeInt(mServiceDataMask == null ? 0 : 1);
-                if (mServiceDataMask != null) {
-                    dest.writeInt(mServiceDataMask.length);
-                    dest.writeByteArray(mServiceDataMask);
-                }
-            }
-        }
-        dest.writeInt(mManufacturerId);
-        dest.writeInt(mManufacturerData == null ? 0 : 1);
-        if (mManufacturerData != null) {
-            dest.writeInt(mManufacturerData.length);
-            dest.writeByteArray(mManufacturerData);
-
-            dest.writeInt(mManufacturerDataMask == null ? 0 : 1);
-            if (mManufacturerDataMask != null) {
-                dest.writeInt(mManufacturerDataMask.length);
-                dest.writeByteArray(mManufacturerDataMask);
-            }
-        }
-
-        // IRK
-        if (mDeviceAddress != null) {
-            dest.writeInt(mAddressType);
-            dest.writeInt(mIrk == null ? 0 : 1);
-            if (mIrk != null) {
-                dest.writeByteArray(mIrk);
-            }
-        }
-    }
-
-    /**
-     * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel.
-     */
-    public static final @android.annotation.NonNull Creator<ScanFilter> CREATOR =
-            new Creator<ScanFilter>() {
-
-        @Override
-        public ScanFilter[] newArray(int size) {
-            return new ScanFilter[size];
-        }
-
-        @Override
-        public ScanFilter createFromParcel(Parcel in) {
-            Builder builder = new Builder();
-            if (in.readInt() == 1) {
-                builder.setDeviceName(in.readString());
-            }
-            String address = null;
-            // If we have a non-null address
-            if (in.readInt() == 1) {
-                address = in.readString();
-            }
-            if (in.readInt() == 1) {
-                ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
-                builder.setServiceUuid(uuid);
-                if (in.readInt() == 1) {
-                    ParcelUuid uuidMask = in.readParcelable(
-                            ParcelUuid.class.getClassLoader());
-                    builder.setServiceUuid(uuid, uuidMask);
-                }
-            }
-            if (in.readInt() == 1) {
-                ParcelUuid solicitationUuid = in.readParcelable(
-                        ParcelUuid.class.getClassLoader());
-                builder.setServiceSolicitationUuid(solicitationUuid);
-                if (in.readInt() == 1) {
-                    ParcelUuid solicitationUuidMask = in.readParcelable(
-                            ParcelUuid.class.getClassLoader());
-                    builder.setServiceSolicitationUuid(solicitationUuid,
-                            solicitationUuidMask);
-                }
-            }
-            if (in.readInt() == 1) {
-                ParcelUuid servcieDataUuid =
-                        in.readParcelable(ParcelUuid.class.getClassLoader());
-                if (in.readInt() == 1) {
-                    int serviceDataLength = in.readInt();
-                    byte[] serviceData = new byte[serviceDataLength];
-                    in.readByteArray(serviceData);
-                    if (in.readInt() == 0) {
-                        builder.setServiceData(servcieDataUuid, serviceData);
-                    } else {
-                        int serviceDataMaskLength = in.readInt();
-                        byte[] serviceDataMask = new byte[serviceDataMaskLength];
-                        in.readByteArray(serviceDataMask);
-                        builder.setServiceData(
-                                servcieDataUuid, serviceData, serviceDataMask);
-                    }
-                }
-            }
-
-            int manufacturerId = in.readInt();
-            if (in.readInt() == 1) {
-                int manufacturerDataLength = in.readInt();
-                byte[] manufacturerData = new byte[manufacturerDataLength];
-                in.readByteArray(manufacturerData);
-                if (in.readInt() == 0) {
-                    builder.setManufacturerData(manufacturerId, manufacturerData);
-                } else {
-                    int manufacturerDataMaskLength = in.readInt();
-                    byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength];
-                    in.readByteArray(manufacturerDataMask);
-                    builder.setManufacturerData(manufacturerId, manufacturerData,
-                            manufacturerDataMask);
-                }
-            }
-
-            // IRK
-            if (address != null) {
-                final int addressType = in.readInt();
-                if (in.readInt() == 1) {
-                    final byte[] irk = new byte[16];
-                    in.readByteArray(irk);
-                    builder.setDeviceAddress(address, addressType, irk);
-                } else {
-                    builder.setDeviceAddress(address, addressType);
-                }
-            }
-            return builder.build();
-        }
-    };
-
-    /**
-     * Returns the filter set the device name field of Bluetooth advertisement data.
-     */
-    @Nullable
-    public String getDeviceName() {
-        return mDeviceName;
-    }
-
-    /**
-     * Returns the filter set on the service uuid.
-     */
-    @Nullable
-    public ParcelUuid getServiceUuid() {
-        return mServiceUuid;
-    }
-
-    @Nullable
-    public ParcelUuid getServiceUuidMask() {
-        return mServiceUuidMask;
-    }
-
-    /**
-     * Returns the filter set on the service Solicitation uuid.
-     */
-    @Nullable
-    public ParcelUuid getServiceSolicitationUuid() {
-        return mServiceSolicitationUuid;
-    }
-
-    /**
-     * Returns the filter set on the service Solicitation uuid mask.
-     */
-    @Nullable
-    public ParcelUuid getServiceSolicitationUuidMask() {
-        return mServiceSolicitationUuidMask;
-    }
-
-    @Nullable
-    public String getDeviceAddress() {
-        return mDeviceAddress;
-    }
-
-    /**
-     * @hide
-     */
-    @SystemApi
-    public @AddressType int getAddressType() {
-        return mAddressType;
-    }
-
-    /**
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    public byte[] getIrk() {
-        return mIrk;
-    }
-
-    @Nullable
-    public byte[] getServiceData() {
-        return mServiceData;
-    }
-
-    @Nullable
-    public byte[] getServiceDataMask() {
-        return mServiceDataMask;
-    }
-
-    @Nullable
-    public ParcelUuid getServiceDataUuid() {
-        return mServiceDataUuid;
-    }
-
-    /**
-     * Returns the manufacturer id. -1 if the manufacturer filter is not set.
-     */
-    public int getManufacturerId() {
-        return mManufacturerId;
-    }
-
-    @Nullable
-    public byte[] getManufacturerData() {
-        return mManufacturerData;
-    }
-
-    @Nullable
-    public byte[] getManufacturerDataMask() {
-        return mManufacturerDataMask;
-    }
-
-    /**
-     * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
-     * if it matches all the field filters.
-     */
-    public boolean matches(ScanResult scanResult) {
-        if (scanResult == null) {
-            return false;
-        }
-        BluetoothDevice device = scanResult.getDevice();
-        // Device match.
-        if (mDeviceAddress != null
-                && (device == null || !mDeviceAddress.equals(device.getAddress()))) {
-            return false;
-        }
-
-        ScanRecord scanRecord = scanResult.getScanRecord();
-
-        // Scan record is null but there exist filters on it.
-        if (scanRecord == null
-                && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null
-                || mServiceData != null || mServiceSolicitationUuid != null)) {
-            return false;
-        }
-
-        // Local name match.
-        if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) {
-            return false;
-        }
-
-        // UUID match.
-        if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask,
-                scanRecord.getServiceUuids())) {
-            return false;
-        }
-
-        // solicitation UUID match.
-        if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids(
-                mServiceSolicitationUuid, mServiceSolicitationUuidMask,
-                scanRecord.getServiceSolicitationUuids())) {
-            return false;
-        }
-
-        // Service data match
-        if (mServiceDataUuid != null) {
-            if (!matchesPartialData(mServiceData, mServiceDataMask,
-                    scanRecord.getServiceData(mServiceDataUuid))) {
-                return false;
-            }
-        }
-
-        // Manufacturer data match.
-        if (mManufacturerId >= 0) {
-            if (!matchesPartialData(mManufacturerData, mManufacturerDataMask,
-                    scanRecord.getManufacturerSpecificData(mManufacturerId))) {
-                return false;
-            }
-        }
-        // All filters match.
-        return true;
-    }
-
-    /**
-     * Check if the uuid pattern is contained in a list of parcel uuids.
-     *
-     * @hide
-     */
-    public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask,
-            List<ParcelUuid> uuids) {
-        if (uuid == null) {
-            return true;
-        }
-        if (uuids == null) {
-            return false;
-        }
-
-        for (ParcelUuid parcelUuid : uuids) {
-            UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid();
-            if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Check if the uuid pattern matches the particular service uuid.
-    private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
-        return BluetoothLeUtils.maskedEquals(data, uuid, mask);
-    }
-
-    /**
-     * Check if the solicitation uuid pattern is contained in a list of parcel uuids.
-     *
-     */
-    private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid,
-            ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids) {
-        if (solicitationUuid == null) {
-            return true;
-        }
-        if (solicitationUuids == null) {
-            return false;
-        }
-
-        for (ParcelUuid parcelSolicitationUuid : solicitationUuids) {
-            UUID solicitationUuidMask = parcelSolicitationUuidMask == null
-                    ? null : parcelSolicitationUuidMask.getUuid();
-            if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask,
-                    parcelSolicitationUuid.getUuid())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Check if the solicitation uuid pattern matches the particular service solicitation uuid.
-    private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
-            UUID solicitationUuidMask, UUID data) {
-        return BluetoothLeUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
-    }
-
-    // Check whether the data pattern matches the parsed data.
-    private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
-        if (parsedData == null || parsedData.length < data.length) {
-            return false;
-        }
-        if (dataMask == null) {
-            for (int i = 0; i < data.length; ++i) {
-                if (parsedData[i] != data[i]) {
-                    return false;
-                }
-            }
-            return true;
-        }
-        for (int i = 0; i < data.length; ++i) {
-            if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress="
-                + mDeviceAddress
-                + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
-                + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid
-                + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask
-                + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData="
-                + Arrays.toString(mServiceData) + ", mServiceDataMask="
-                + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
-                + ", mManufacturerData=" + Arrays.toString(mManufacturerData)
-                + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]";
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId,
-                Arrays.hashCode(mManufacturerData),
-                Arrays.hashCode(mManufacturerDataMask),
-                mServiceDataUuid,
-                Arrays.hashCode(mServiceData),
-                Arrays.hashCode(mServiceDataMask),
-                mServiceUuid, mServiceUuidMask,
-                mServiceSolicitationUuid, mServiceSolicitationUuidMask);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        ScanFilter other = (ScanFilter) obj;
-        return Objects.equals(mDeviceName, other.mDeviceName)
-                && Objects.equals(mDeviceAddress, other.mDeviceAddress)
-                && mManufacturerId == other.mManufacturerId
-                && Objects.deepEquals(mManufacturerData, other.mManufacturerData)
-                && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask)
-                && Objects.equals(mServiceDataUuid, other.mServiceDataUuid)
-                && Objects.deepEquals(mServiceData, other.mServiceData)
-                && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask)
-                && Objects.equals(mServiceUuid, other.mServiceUuid)
-                && Objects.equals(mServiceUuidMask, other.mServiceUuidMask)
-                && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid)
-                && Objects.equals(mServiceSolicitationUuidMask,
-                        other.mServiceSolicitationUuidMask);
-    }
-
-    /**
-     * Checks if the scanfilter is empty
-     *
-     * @hide
-     */
-    public boolean isAllFieldsEmpty() {
-        return EMPTY.equals(this);
-    }
-
-    /**
-     * Builder class for {@link ScanFilter}.
-     */
-    public static final class Builder {
-
-        /**
-         * @hide
-         */
-        @SystemApi
-        public static final int LEN_IRK_OCTETS = 16;
-
-        private String mDeviceName;
-        private String mDeviceAddress;
-        private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC;
-        private byte[] mIrk;
-
-        private ParcelUuid mServiceUuid;
-        private ParcelUuid mUuidMask;
-
-        private ParcelUuid mServiceSolicitationUuid;
-        private ParcelUuid mServiceSolicitationUuidMask;
-
-        private ParcelUuid mServiceDataUuid;
-        private byte[] mServiceData;
-        private byte[] mServiceDataMask;
-
-        private int mManufacturerId = -1;
-        private byte[] mManufacturerData;
-        private byte[] mManufacturerDataMask;
-
-        /**
-         * Set filter on device name.
-         */
-        public Builder setDeviceName(String deviceName) {
-            mDeviceName = deviceName;
-            return this;
-        }
-
-        /**
-         * Set filter on device address.
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.  The @AddressType is defaulted to {@link
-         * BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         */
-        public Builder setDeviceAddress(String deviceAddress) {
-            if (deviceAddress == null) {
-                mDeviceAddress = deviceAddress;
-                return this;
-            }
-            return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
-        }
-
-        /**
-         * Set filter on Address with AddressType
-         *
-         * <p>This key is used to resolve a private address from a public address.
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}. May be any type of address.
-         * @param addressType indication of the type of address
-         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
-         *
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         * @throws IllegalArgumentException If the {@code addressType} is invalid length
-         * @throws NullPointerException if {@code deviceAddress} is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public Builder setDeviceAddress(@NonNull String deviceAddress,
-                                        @AddressType int addressType) {
-            return setDeviceAddressInternal(deviceAddress, addressType, null);
-        }
-
-        /**
-         * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
-         *
-         * <p>The IRK is used to resolve a {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} from
-         * a PRIVATE_ADDRESS type.
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.  This Address type must only be PUBLIC OR RANDOM
-         * STATIC.
-         * @param addressType indication of the type of address
-         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
-         * @param irk non-null byte array representing the Identity Resolving Key
-         *
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         * @throws IllegalArgumentException if the {@code irk} is invalid length.
-         * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not
-         * PUBLIC or RANDOM STATIC when an IRK is present.
-         * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null.
-         *
-         * @hide
-         */
-        @NonNull
-        @SystemApi
-        public Builder setDeviceAddress(@NonNull String deviceAddress,
-                                        @AddressType int addressType,
-                                        @NonNull byte[] irk) {
-            requireNonNull(irk);
-            if (irk.length != LEN_IRK_OCTETS) {
-                throw new IllegalArgumentException("'irk' is invalid length!");
-            }
-            return setDeviceAddressInternal(deviceAddress, addressType, irk);
-        }
-
-        /**
-         * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
-         *
-         * <p>Internal setter for the device address
-         *
-         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
-         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.
-         * @param addressType indication of the type of address
-         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
-         * @param irk non-null byte array representing the Identity Resolving Address; nullable
-         * internally.
-         *
-         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
-         * @throws IllegalArgumentException If the {@code addressType} is invalid length.
-         * @throws NullPointerException if {@code deviceAddress} is null.
-         *
-         * @hide
-         */
-        @NonNull
-        private Builder setDeviceAddressInternal(@NonNull String deviceAddress,
-                                                 @AddressType int addressType,
-                                                 @Nullable byte[] irk) {
-
-            // Make sure our deviceAddress is valid!
-            requireNonNull(deviceAddress);
-            if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
-                throw new IllegalArgumentException("invalid device address " + deviceAddress);
-            }
-
-            // Verify type range
-            if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC
-                || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) {
-                throw new IllegalArgumentException("'addressType' is invalid!");
-            }
-
-            // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address.
-            if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) {
-                // Don't want a bad combination of address and irk!
-                if (irk != null) {
-                    // Since there are 3 possible RANDOM subtypes we must check to make sure
-                    // the correct type of address is used.
-                    if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) {
-                        throw new IllegalArgumentException(
-                                "Invalid combination: IRK requires either a PUBLIC or "
-                                + "RANDOM (STATIC) Address");
-                    }
-                }
-            }
-
-            // PUBLIC doesn't require extra work
-            // Without an IRK any address may be accepted
-
-            mDeviceAddress = deviceAddress;
-            mAddressType = addressType;
-            mIrk = irk;
-            return this;
-        }
-
-        /**
-         * Set filter on service uuid.
-         */
-        public Builder setServiceUuid(ParcelUuid serviceUuid) {
-            mServiceUuid = serviceUuid;
-            mUuidMask = null; // clear uuid mask
-            return this;
-        }
-
-        /**
-         * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the
-         * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the
-         * bit in {@code serviceUuid}, and 0 to ignore that bit.
-         *
-         * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code
-         * uuidMask} is not {@code null}.
-         */
-        public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) {
-            if (mUuidMask != null && mServiceUuid == null) {
-                throw new IllegalArgumentException("uuid is null while uuidMask is not null!");
-            }
-            mServiceUuid = serviceUuid;
-            mUuidMask = uuidMask;
-            return this;
-        }
-
-
-        /**
-         * Set filter on service solicitation uuid.
-         */
-        public @NonNull Builder setServiceSolicitationUuid(
-                @Nullable ParcelUuid serviceSolicitationUuid) {
-            mServiceSolicitationUuid = serviceSolicitationUuid;
-            if (serviceSolicitationUuid == null) {
-                mServiceSolicitationUuidMask = null;
-            }
-            return this;
-        }
-
-
-        /**
-         * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the
-         * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to
-         * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to
-         * ignore that bit.
-         *
-         * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null.
-         * @param solicitationUuidMask can be null or a mask with no restriction.
-         *
-         * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
-         *             {@code serviceSolicitationUuidMask} is not {@code null}.
-         */
-        public @NonNull Builder setServiceSolicitationUuid(
-                @Nullable ParcelUuid serviceSolicitationUuid,
-                @Nullable ParcelUuid solicitationUuidMask) {
-            if (solicitationUuidMask != null && serviceSolicitationUuid == null) {
-                throw new IllegalArgumentException(
-                        "SolicitationUuid is null while SolicitationUuidMask is not null!");
-            }
-            mServiceSolicitationUuid = serviceSolicitationUuid;
-            mServiceSolicitationUuidMask = solicitationUuidMask;
-            return this;
-        }
-
-        /**
-         * Set filtering on service data.
-         *
-         * @throws IllegalArgumentException If {@code serviceDataUuid} is null.
-         */
-        public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
-            if (serviceDataUuid == null) {
-                throw new IllegalArgumentException("serviceDataUuid is null");
-            }
-            mServiceDataUuid = serviceDataUuid;
-            mServiceData = serviceData;
-            mServiceDataMask = null; // clear service data mask
-            return this;
-        }
-
-        /**
-         * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to
-         * match the one in service data, otherwise set it to 0 to ignore that bit.
-         * <p>
-         * The {@code serviceDataMask} must have the same length of the {@code serviceData}.
-         *
-         * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code
-         * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code
-         * serviceDataMask} and {@code serviceData} has different length.
-         */
-        public Builder setServiceData(ParcelUuid serviceDataUuid,
-                byte[] serviceData, byte[] serviceDataMask) {
-            if (serviceDataUuid == null) {
-                throw new IllegalArgumentException("serviceDataUuid is null");
-            }
-            if (mServiceDataMask != null) {
-                if (mServiceData == null) {
-                    throw new IllegalArgumentException(
-                            "serviceData is null while serviceDataMask is not null");
-                }
-                // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two
-                // byte array need to be the same.
-                if (mServiceData.length != mServiceDataMask.length) {
-                    throw new IllegalArgumentException(
-                            "size mismatch for service data and service data mask");
-                }
-            }
-            mServiceDataUuid = serviceDataUuid;
-            mServiceData = serviceData;
-            mServiceDataMask = serviceDataMask;
-            return this;
-        }
-
-        /**
-         * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
-         *
-         * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
-         */
-        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) {
-            if (manufacturerData != null && manufacturerId < 0) {
-                throw new IllegalArgumentException("invalid manufacture id");
-            }
-            mManufacturerId = manufacturerId;
-            mManufacturerData = manufacturerData;
-            mManufacturerDataMask = null; // clear manufacturer data mask
-            return this;
-        }
-
-        /**
-         * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs
-         * to match the one in manufacturer data, otherwise set it to 0.
-         * <p>
-         * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}.
-         *
-         * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code
-         * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code
-         * manufacturerData} and {@code manufacturerDataMask} have different length.
-         */
-        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData,
-                byte[] manufacturerDataMask) {
-            if (manufacturerData != null && manufacturerId < 0) {
-                throw new IllegalArgumentException("invalid manufacture id");
-            }
-            if (mManufacturerDataMask != null) {
-                if (mManufacturerData == null) {
-                    throw new IllegalArgumentException(
-                            "manufacturerData is null while manufacturerDataMask is not null");
-                }
-                // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths
-                // of the two byte array need to be the same.
-                if (mManufacturerData.length != mManufacturerDataMask.length) {
-                    throw new IllegalArgumentException(
-                            "size mismatch for manufacturerData and manufacturerDataMask");
-                }
-            }
-            mManufacturerId = manufacturerId;
-            mManufacturerData = manufacturerData;
-            mManufacturerDataMask = manufacturerDataMask;
-            return this;
-        }
-
-        /**
-         * Build {@link ScanFilter}.
-         *
-         * @throws IllegalArgumentException If the filter cannot be built.
-         */
-        public ScanFilter build() {
-            return new ScanFilter(mDeviceName, mDeviceAddress,
-                    mServiceUuid, mUuidMask, mServiceSolicitationUuid,
-                    mServiceSolicitationUuidMask,
-                    mServiceDataUuid, mServiceData, mServiceDataMask,
-                    mManufacturerId, mManufacturerData, mManufacturerDataMask,
-                    mAddressType, mIrk);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
deleted file mode 100644
index 9b8c2ea..0000000
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.bluetooth.BluetoothUuid;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.ParcelUuid;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-
-/**
- * Represents a scan record from Bluetooth LE scan.
- */
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class ScanRecord {
-
-    private static final String TAG = "ScanRecord";
-
-    // The following data type values are assigned by Bluetooth SIG.
-    // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.
-    private static final int DATA_TYPE_FLAGS = 0x01;
-    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
-    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
-    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
-    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
-    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
-    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
-    private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
-    private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
-    private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
-    private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
-    private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
-    private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
-    private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14;
-    private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F;
-    private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15;
-    private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
-
-    // Flags of the advertising data.
-    private final int mAdvertiseFlags;
-
-    @Nullable
-    private final List<ParcelUuid> mServiceUuids;
-    @Nullable
-    private final List<ParcelUuid> mServiceSolicitationUuids;
-
-    private final SparseArray<byte[]> mManufacturerSpecificData;
-
-    private final Map<ParcelUuid, byte[]> mServiceData;
-
-    // Transmission power level(in dB).
-    private final int mTxPowerLevel;
-
-    // Local name of the Bluetooth LE device.
-    private final String mDeviceName;
-
-    // Raw bytes of scan record.
-    private final byte[] mBytes;
-
-    /**
-     * Returns the advertising flags indicating the discoverable mode and capability of the device.
-     * Returns -1 if the flag field is not set.
-     */
-    public int getAdvertiseFlags() {
-        return mAdvertiseFlags;
-    }
-
-    /**
-     * Returns a list of service UUIDs within the advertisement that are used to identify the
-     * bluetooth GATT services.
-     */
-    public List<ParcelUuid> getServiceUuids() {
-        return mServiceUuids;
-    }
-
-    /**
-     * Returns a list of service solicitation UUIDs within the advertisement that are used to
-     * identify the Bluetooth GATT services.
-     */
-    @NonNull
-    public List<ParcelUuid> getServiceSolicitationUuids() {
-        return mServiceSolicitationUuids;
-    }
-
-    /**
-     * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
-     * data.
-     */
-    public SparseArray<byte[]> getManufacturerSpecificData() {
-        return mManufacturerSpecificData;
-    }
-
-    /**
-     * Returns the manufacturer specific data associated with the manufacturer id. Returns
-     * {@code null} if the {@code manufacturerId} is not found.
-     */
-    @Nullable
-    public byte[] getManufacturerSpecificData(int manufacturerId) {
-        if (mManufacturerSpecificData == null) {
-            return null;
-        }
-        return mManufacturerSpecificData.get(manufacturerId);
-    }
-
-    /**
-     * Returns a map of service UUID and its corresponding service data.
-     */
-    public Map<ParcelUuid, byte[]> getServiceData() {
-        return mServiceData;
-    }
-
-    /**
-     * Returns the service data byte array associated with the {@code serviceUuid}. Returns
-     * {@code null} if the {@code serviceDataUuid} is not found.
-     */
-    @Nullable
-    public byte[] getServiceData(ParcelUuid serviceDataUuid) {
-        if (serviceDataUuid == null || mServiceData == null) {
-            return null;
-        }
-        return mServiceData.get(serviceDataUuid);
-    }
-
-    /**
-     * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
-     * if the field is not set. This value can be used to calculate the path loss of a received
-     * packet using the following equation:
-     * <p>
-     * <code>pathloss = txPowerLevel - rssi</code>
-     */
-    public int getTxPowerLevel() {
-        return mTxPowerLevel;
-    }
-
-    /**
-     * Returns the local name of the BLE device. This is a UTF-8 encoded string.
-     */
-    @Nullable
-    public String getDeviceName() {
-        return mDeviceName;
-    }
-
-    /**
-     * Returns raw bytes of scan record.
-     */
-    public byte[] getBytes() {
-        return mBytes;
-    }
-
-    /**
-     * Test if any fields contained inside this scan record are matched by the
-     * given matcher.
-     *
-     * @hide
-     */
-    public boolean matchesAnyField(@NonNull Predicate<byte[]> matcher) {
-        int pos = 0;
-        while (pos < mBytes.length) {
-            final int length = mBytes[pos] & 0xFF;
-            if (length == 0) {
-                break;
-            }
-            if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) {
-                return true;
-            }
-            pos += length + 1;
-        }
-        return false;
-    }
-
-    private ScanRecord(List<ParcelUuid> serviceUuids,
-            List<ParcelUuid> serviceSolicitationUuids,
-            SparseArray<byte[]> manufacturerData,
-            Map<ParcelUuid, byte[]> serviceData,
-            int advertiseFlags, int txPowerLevel,
-            String localName, byte[] bytes) {
-        mServiceSolicitationUuids = serviceSolicitationUuids;
-        mServiceUuids = serviceUuids;
-        mManufacturerSpecificData = manufacturerData;
-        mServiceData = serviceData;
-        mDeviceName = localName;
-        mAdvertiseFlags = advertiseFlags;
-        mTxPowerLevel = txPowerLevel;
-        mBytes = bytes;
-    }
-
-    /**
-     * Parse scan record bytes to {@link ScanRecord}.
-     * <p>
-     * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
-     * <p>
-     * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
-     * order.
-     *
-     * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static ScanRecord parseFromBytes(byte[] scanRecord) {
-        if (scanRecord == null) {
-            return null;
-        }
-
-        int currentPos = 0;
-        int advertiseFlag = -1;
-        List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
-        List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>();
-        String localName = null;
-        int txPowerLevel = Integer.MIN_VALUE;
-
-        SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
-        Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
-
-        try {
-            while (currentPos < scanRecord.length) {
-                // length is unsigned int.
-                int length = scanRecord[currentPos++] & 0xFF;
-                if (length == 0) {
-                    break;
-                }
-                // Note the length includes the length of the field type itself.
-                int dataLength = length - 1;
-                // fieldType is unsigned int.
-                int fieldType = scanRecord[currentPos++] & 0xFF;
-                switch (fieldType) {
-                    case DATA_TYPE_FLAGS:
-                        advertiseFlag = scanRecord[currentPos] & 0xFF;
-                        break;
-                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
-                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
-                        parseServiceUuid(scanRecord, currentPos,
-                                dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
-                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
-                        parseServiceUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
-                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
-                        parseServiceUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT:
-                        parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT:
-                        parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids);
-                        break;
-                    case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT:
-                        parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
-                                BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids);
-                        break;
-                    case DATA_TYPE_LOCAL_NAME_SHORT:
-                    case DATA_TYPE_LOCAL_NAME_COMPLETE:
-                        localName = new String(
-                                extractBytes(scanRecord, currentPos, dataLength));
-                        break;
-                    case DATA_TYPE_TX_POWER_LEVEL:
-                        txPowerLevel = scanRecord[currentPos];
-                        break;
-                    case DATA_TYPE_SERVICE_DATA_16_BIT:
-                    case DATA_TYPE_SERVICE_DATA_32_BIT:
-                    case DATA_TYPE_SERVICE_DATA_128_BIT:
-                        int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
-                        if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
-                            serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
-                        } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
-                            serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
-                        }
-
-                        byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
-                                serviceUuidLength);
-                        ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
-                                serviceDataUuidBytes);
-                        byte[] serviceDataArray = extractBytes(scanRecord,
-                                currentPos + serviceUuidLength, dataLength - serviceUuidLength);
-                        serviceData.put(serviceDataUuid, serviceDataArray);
-                        break;
-                    case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
-                        // The first two bytes of the manufacturer specific data are
-                        // manufacturer ids in little endian.
-                        int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8)
-                                + (scanRecord[currentPos] & 0xFF);
-                        byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
-                                dataLength - 2);
-                        manufacturerData.put(manufacturerId, manufacturerDataBytes);
-                        break;
-                    default:
-                        // Just ignore, we don't handle such data type.
-                        break;
-                }
-                currentPos += dataLength;
-            }
-
-            if (serviceUuids.isEmpty()) {
-                serviceUuids = null;
-            }
-            return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
-                    serviceData, advertiseFlag, txPowerLevel, localName, scanRecord);
-        } catch (Exception e) {
-            Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
-            // As the record is invalid, ignore all the parsed results for this packet
-            // and return an empty record with raw scanRecord bytes in results
-            return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
-                + ", mServiceSolicitationUuids=" + mServiceSolicitationUuids
-                + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(
-                mManufacturerSpecificData)
-                + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
-                + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
-    }
-
-    // Parse service UUIDs.
-    private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
-            int uuidLength, List<ParcelUuid> serviceUuids) {
-        while (dataLength > 0) {
-            byte[] uuidBytes = extractBytes(scanRecord, currentPos,
-                    uuidLength);
-            serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
-            dataLength -= uuidLength;
-            currentPos += uuidLength;
-        }
-        return currentPos;
-    }
-
-    /**
-     * Parse service Solicitation UUIDs.
-     */
-    private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos,
-            int dataLength, int uuidLength, List<ParcelUuid> serviceSolicitationUuids) {
-        while (dataLength > 0) {
-            byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength);
-            serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
-            dataLength -= uuidLength;
-            currentPos += uuidLength;
-        }
-        return currentPos;
-    }
-
-    // Helper method to extract bytes from byte array.
-    private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
-        byte[] bytes = new byte[length];
-        System.arraycopy(scanRecord, start, bytes, 0, length);
-        return bytes;
-    }
-}
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
deleted file mode 100644
index f437d86..0000000
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.Attributable;
-import android.bluetooth.BluetoothDevice;
-import android.content.AttributionSource;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * ScanResult for Bluetooth LE scan.
- */
-public final class ScanResult implements Parcelable, Attributable {
-
-    /**
-     * For chained advertisements, inidcates tha the data contained in this
-     * scan result is complete.
-     */
-    public static final int DATA_COMPLETE = 0x00;
-
-    /**
-     * For chained advertisements, indicates that the controller was
-     * unable to receive all chained packets and the scan result contains
-     * incomplete truncated data.
-     */
-    public static final int DATA_TRUNCATED = 0x02;
-
-    /**
-     * Indicates that the secondary physical layer was not used.
-     */
-    public static final int PHY_UNUSED = 0x00;
-
-    /**
-     * Advertising Set ID is not present in the packet.
-     */
-    public static final int SID_NOT_PRESENT = 0xFF;
-
-    /**
-     * TX power is not present in the packet.
-     */
-    public static final int TX_POWER_NOT_PRESENT = 0x7F;
-
-    /**
-     * Periodic advertising interval is not present in the packet.
-     */
-    public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00;
-
-    /**
-     * Mask for checking whether event type represents legacy advertisement.
-     */
-    private static final int ET_LEGACY_MASK = 0x10;
-
-    /**
-     * Mask for checking whether event type represents connectable advertisement.
-     */
-    private static final int ET_CONNECTABLE_MASK = 0x01;
-
-    // Remote Bluetooth device.
-    private BluetoothDevice mDevice;
-
-    // Scan record, including advertising data and scan response data.
-    @Nullable
-    private ScanRecord mScanRecord;
-
-    // Received signal strength.
-    private int mRssi;
-
-    // Device timestamp when the result was last seen.
-    private long mTimestampNanos;
-
-    private int mEventType;
-    private int mPrimaryPhy;
-    private int mSecondaryPhy;
-    private int mAdvertisingSid;
-    private int mTxPower;
-    private int mPeriodicAdvertisingInterval;
-
-    /**
-     * Constructs a new ScanResult.
-     *
-     * @param device Remote Bluetooth device found.
-     * @param scanRecord Scan record including both advertising data and scan response data.
-     * @param rssi Received signal strength.
-     * @param timestampNanos Timestamp at which the scan result was observed.
-     * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int,
-     * ScanRecord, long)}
-     */
-    @Deprecated
-    public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
-            long timestampNanos) {
-        mDevice = device;
-        mScanRecord = scanRecord;
-        mRssi = rssi;
-        mTimestampNanos = timestampNanos;
-        mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
-        mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
-        mSecondaryPhy = PHY_UNUSED;
-        mAdvertisingSid = SID_NOT_PRESENT;
-        mTxPower = 127;
-        mPeriodicAdvertisingInterval = 0;
-    }
-
-    /**
-     * Constructs a new ScanResult.
-     *
-     * @param device Remote Bluetooth device found.
-     * @param eventType Event type.
-     * @param primaryPhy Primary advertising phy.
-     * @param secondaryPhy Secondary advertising phy.
-     * @param advertisingSid Advertising set ID.
-     * @param txPower Transmit power.
-     * @param rssi Received signal strength.
-     * @param periodicAdvertisingInterval Periodic advertising interval.
-     * @param scanRecord Scan record including both advertising data and scan response data.
-     * @param timestampNanos Timestamp at which the scan result was observed.
-     */
-    public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
-            int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
-            ScanRecord scanRecord, long timestampNanos) {
-        mDevice = device;
-        mEventType = eventType;
-        mPrimaryPhy = primaryPhy;
-        mSecondaryPhy = secondaryPhy;
-        mAdvertisingSid = advertisingSid;
-        mTxPower = txPower;
-        mRssi = rssi;
-        mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
-        mScanRecord = scanRecord;
-        mTimestampNanos = timestampNanos;
-    }
-
-    private ScanResult(Parcel in) {
-        readFromParcel(in);
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        if (mDevice != null) {
-            dest.writeInt(1);
-            mDevice.writeToParcel(dest, flags);
-        } else {
-            dest.writeInt(0);
-        }
-        if (mScanRecord != null) {
-            dest.writeInt(1);
-            dest.writeByteArray(mScanRecord.getBytes());
-        } else {
-            dest.writeInt(0);
-        }
-        dest.writeInt(mRssi);
-        dest.writeLong(mTimestampNanos);
-        dest.writeInt(mEventType);
-        dest.writeInt(mPrimaryPhy);
-        dest.writeInt(mSecondaryPhy);
-        dest.writeInt(mAdvertisingSid);
-        dest.writeInt(mTxPower);
-        dest.writeInt(mPeriodicAdvertisingInterval);
-    }
-
-    private void readFromParcel(Parcel in) {
-        if (in.readInt() == 1) {
-            mDevice = BluetoothDevice.CREATOR.createFromParcel(in);
-        }
-        if (in.readInt() == 1) {
-            mScanRecord = ScanRecord.parseFromBytes(in.createByteArray());
-        }
-        mRssi = in.readInt();
-        mTimestampNanos = in.readLong();
-        mEventType = in.readInt();
-        mPrimaryPhy = in.readInt();
-        mSecondaryPhy = in.readInt();
-        mAdvertisingSid = in.readInt();
-        mTxPower = in.readInt();
-        mPeriodicAdvertisingInterval = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** {@hide} */
-    public void setAttributionSource(@NonNull AttributionSource attributionSource) {
-        Attributable.setAttributionSource(mDevice, attributionSource);
-    }
-
-    /**
-     * Returns the remote Bluetooth device identified by the Bluetooth device address.
-     */
-    public BluetoothDevice getDevice() {
-        return mDevice;
-    }
-
-    /**
-     * Returns the scan record, which is a combination of advertisement and scan response.
-     */
-    @Nullable
-    public ScanRecord getScanRecord() {
-        return mScanRecord;
-    }
-
-    /**
-     * Returns the received signal strength in dBm. The valid range is [-127, 126].
-     */
-    public int getRssi() {
-        return mRssi;
-    }
-
-    /**
-     * Returns timestamp since boot when the scan record was observed.
-     */
-    public long getTimestampNanos() {
-        return mTimestampNanos;
-    }
-
-    /**
-     * Returns true if this object represents legacy scan result.
-     * Legacy scan results do not contain advanced advertising information
-     * as specified in the Bluetooth Core Specification v5.
-     */
-    public boolean isLegacy() {
-        return (mEventType & ET_LEGACY_MASK) != 0;
-    }
-
-    /**
-     * Returns true if this object represents connectable scan result.
-     */
-    public boolean isConnectable() {
-        return (mEventType & ET_CONNECTABLE_MASK) != 0;
-    }
-
-    /**
-     * Returns the data status.
-     * Can be one of {@link ScanResult#DATA_COMPLETE} or
-     * {@link ScanResult#DATA_TRUNCATED}.
-     */
-    public int getDataStatus() {
-        // return bit 5 and 6
-        return (mEventType >> 5) & 0x03;
-    }
-
-    /**
-     * Returns the primary Physical Layer
-     * on which this advertisment was received.
-     * Can be one of {@link BluetoothDevice#PHY_LE_1M} or
-     * {@link BluetoothDevice#PHY_LE_CODED}.
-     */
-    public int getPrimaryPhy() {
-        return mPrimaryPhy;
-    }
-
-    /**
-     * Returns the secondary Physical Layer
-     * on which this advertisment was received.
-     * Can be one of {@link BluetoothDevice#PHY_LE_1M},
-     * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED}
-     * or {@link ScanResult#PHY_UNUSED} - if the advertisement
-     * was not received on a secondary physical channel.
-     */
-    public int getSecondaryPhy() {
-        return mSecondaryPhy;
-    }
-
-    /**
-     * Returns the advertising set id.
-     * May return {@link ScanResult#SID_NOT_PRESENT} if
-     * no set id was is present.
-     */
-    public int getAdvertisingSid() {
-        return mAdvertisingSid;
-    }
-
-    /**
-     * Returns the transmit power in dBm.
-     * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT}
-     * indicates that the TX power is not present.
-     */
-    public int getTxPower() {
-        return mTxPower;
-    }
-
-    /**
-     * Returns the periodic advertising interval in units of 1.25ms.
-     * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of
-     * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic
-     * advertising interval is not present.
-     */
-    public int getPeriodicAdvertisingInterval() {
-        return mPeriodicAdvertisingInterval;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
-                mEventType, mPrimaryPhy, mSecondaryPhy,
-                mAdvertisingSid, mTxPower,
-                mPeriodicAdvertisingInterval);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        ScanResult other = (ScanResult) obj;
-        return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi)
-                && Objects.equals(mScanRecord, other.mScanRecord)
-                && (mTimestampNanos == other.mTimestampNanos)
-                && mEventType == other.mEventType
-                && mPrimaryPhy == other.mPrimaryPhy
-                && mSecondaryPhy == other.mSecondaryPhy
-                && mAdvertisingSid == other.mAdvertisingSid
-                && mTxPower == other.mTxPower
-                && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
-    }
-
-    @Override
-    public String toString() {
-        return "ScanResult{" + "device=" + mDevice + ", scanRecord="
-                + Objects.toString(mScanRecord) + ", rssi=" + mRssi
-                + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType
-                + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy
-                + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower
-                + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
-        @Override
-        public ScanResult createFromParcel(Parcel source) {
-            return new ScanResult(source);
-        }
-
-        @Override
-        public ScanResult[] newArray(int size) {
-            return new ScanResult[size];
-        }
-    };
-
-}
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
deleted file mode 100644
index 1aa7cb5..0000000
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.SystemApi;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the
- * parameters for the scan.
- */
-public final class ScanSettings implements Parcelable {
-
-    /**
-     * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
-     * other scan results without starting BLE scans themselves.
-     */
-    public static final int SCAN_MODE_OPPORTUNISTIC = -1;
-
-    /**
-     * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
-     * least power. This mode is enforced if the scanning application is not in foreground.
-     */
-    public static final int SCAN_MODE_LOW_POWER = 0;
-
-    /**
-     * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that
-     * provides a good trade-off between scan frequency and power consumption.
-     */
-    public static final int SCAN_MODE_BALANCED = 1;
-
-    /**
-     * Scan using highest duty cycle. It's recommended to only use this mode when the application is
-     * running in the foreground.
-     */
-    public static final int SCAN_MODE_LOW_LATENCY = 2;
-
-    /**
-     * Perform Bluetooth LE scan in ambient discovery mode. This mode has lower duty cycle and more
-     * aggressive scan interval than balanced mode that provides a good trade-off between scan
-     * latency and power consumption.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3;
-
-    /**
-     * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
-     * If no filter is active, all advertisement packets are reported.
-     */
-    public static final int CALLBACK_TYPE_ALL_MATCHES = 1;
-
-    /**
-     * A result callback is only triggered for the first advertisement packet received that matches
-     * the filter criteria.
-     */
-    public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
-
-    /**
-     * Receive a callback when advertisements are no longer received from a device that has been
-     * previously reported by a first match callback.
-     */
-    public static final int CALLBACK_TYPE_MATCH_LOST = 4;
-
-
-    /**
-     * Determines how many advertisements to match per filter, as this is scarce hw resource
-     */
-    /**
-     * Match one advertisement per filter
-     */
-    public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
-
-    /**
-     * Match few advertisement per filter, depends on current capability and availibility of
-     * the resources in hw
-     */
-    public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
-
-    /**
-     * Match as many advertisement per filter as hw could allow, depends on current
-     * capability and availibility of the resources in hw
-     */
-    public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
-
-
-    /**
-     * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
-     * and few number of sightings/match in a duration.
-     */
-    public static final int MATCH_MODE_AGGRESSIVE = 1;
-
-    /**
-     * For sticky mode, higher threshold of signal strength and sightings is required
-     * before reporting by hw
-     */
-    public static final int MATCH_MODE_STICKY = 2;
-
-    /**
-     * Request full scan results which contain the device, rssi, advertising data, scan response
-     * as well as the scan timestamp.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int SCAN_RESULT_TYPE_FULL = 0;
-
-    /**
-     * Request abbreviated scan results which contain the device, rssi and scan timestamp.
-     * <p>
-     * <b>Note:</b> It is possible for an application to get more scan results than it asked for, if
-     * there are multiple apps using this type.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1;
-
-    /**
-     * Use all supported PHYs for scanning.
-     * This will check the controller capabilities, and start
-     * the scan on 1Mbit and LE Coded PHYs if supported, or on
-     * the 1Mbit PHY only.
-     */
-    public static final int PHY_LE_ALL_SUPPORTED = 255;
-
-    // Bluetooth LE scan mode.
-    private int mScanMode;
-
-    // Bluetooth LE scan callback type
-    private int mCallbackType;
-
-    // Bluetooth LE scan result type
-    private int mScanResultType;
-
-    // Time of delay for reporting the scan result
-    private long mReportDelayMillis;
-
-    private int mMatchMode;
-
-    private int mNumOfMatchesPerFilter;
-
-    // Include only legacy advertising results
-    private boolean mLegacy;
-
-    private int mPhy;
-
-    public int getScanMode() {
-        return mScanMode;
-    }
-
-    public int getCallbackType() {
-        return mCallbackType;
-    }
-
-    public int getScanResultType() {
-        return mScanResultType;
-    }
-
-    /**
-     * @hide
-     */
-    public int getMatchMode() {
-        return mMatchMode;
-    }
-
-    /**
-     * @hide
-     */
-    public int getNumOfMatches() {
-        return mNumOfMatchesPerFilter;
-    }
-
-    /**
-     * Returns whether only legacy advertisements will be returned.
-     * Legacy advertisements include advertisements as specified
-     * by the Bluetooth core specification 4.2 and below.
-     */
-    public boolean getLegacy() {
-        return mLegacy;
-    }
-
-    /**
-     * Returns the physical layer used during a scan.
-     */
-    public int getPhy() {
-        return mPhy;
-    }
-
-    /**
-     * Returns report delay timestamp based on the device clock.
-     */
-    public long getReportDelayMillis() {
-        return mReportDelayMillis;
-    }
-
-    private ScanSettings(int scanMode, int callbackType, int scanResultType,
-            long reportDelayMillis, int matchMode,
-            int numOfMatchesPerFilter, boolean legacy, int phy) {
-        mScanMode = scanMode;
-        mCallbackType = callbackType;
-        mScanResultType = scanResultType;
-        mReportDelayMillis = reportDelayMillis;
-        mNumOfMatchesPerFilter = numOfMatchesPerFilter;
-        mMatchMode = matchMode;
-        mLegacy = legacy;
-        mPhy = phy;
-    }
-
-    private ScanSettings(Parcel in) {
-        mScanMode = in.readInt();
-        mCallbackType = in.readInt();
-        mScanResultType = in.readInt();
-        mReportDelayMillis = in.readLong();
-        mMatchMode = in.readInt();
-        mNumOfMatchesPerFilter = in.readInt();
-        mLegacy = in.readInt() != 0;
-        mPhy = in.readInt();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mScanMode);
-        dest.writeInt(mCallbackType);
-        dest.writeInt(mScanResultType);
-        dest.writeLong(mReportDelayMillis);
-        dest.writeInt(mMatchMode);
-        dest.writeInt(mNumOfMatchesPerFilter);
-        dest.writeInt(mLegacy ? 1 : 0);
-        dest.writeInt(mPhy);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<ScanSettings> CREATOR =
-            new Creator<ScanSettings>() {
-        @Override
-        public ScanSettings[] newArray(int size) {
-            return new ScanSettings[size];
-        }
-
-        @Override
-        public ScanSettings createFromParcel(Parcel in) {
-            return new ScanSettings(in);
-        }
-    };
-
-    /**
-     * Builder for {@link ScanSettings}.
-     */
-    public static final class Builder {
-        private int mScanMode = SCAN_MODE_LOW_POWER;
-        private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
-        private int mScanResultType = SCAN_RESULT_TYPE_FULL;
-        private long mReportDelayMillis = 0;
-        private int mMatchMode = MATCH_MODE_AGGRESSIVE;
-        private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
-        private boolean mLegacy = true;
-        private int mPhy = PHY_LE_ALL_SUPPORTED;
-
-        /**
-         * Set scan mode for Bluetooth LE scan.
-         *
-         * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER},
-         * {@link ScanSettings#SCAN_MODE_BALANCED} or {@link ScanSettings#SCAN_MODE_LOW_LATENCY}.
-         * @throws IllegalArgumentException If the {@code scanMode} is invalid.
-         */
-        public Builder setScanMode(int scanMode) {
-            switch (scanMode) {
-                case SCAN_MODE_OPPORTUNISTIC:
-                case SCAN_MODE_LOW_POWER:
-                case SCAN_MODE_BALANCED:
-                case SCAN_MODE_LOW_LATENCY:
-                case SCAN_MODE_AMBIENT_DISCOVERY:
-                    mScanMode = scanMode;
-                    break;
-                default:
-                    throw new IllegalArgumentException("invalid scan mode " + scanMode);
-            }
-            return this;
-        }
-
-        /**
-         * Set callback type for Bluetooth LE scan.
-         *
-         * @param callbackType The callback type flags for the scan.
-         * @throws IllegalArgumentException If the {@code callbackType} is invalid.
-         */
-        public Builder setCallbackType(int callbackType) {
-
-            if (!isValidCallbackType(callbackType)) {
-                throw new IllegalArgumentException("invalid callback type - " + callbackType);
-            }
-            mCallbackType = callbackType;
-            return this;
-        }
-
-        // Returns true if the callbackType is valid.
-        private boolean isValidCallbackType(int callbackType) {
-            if (callbackType == CALLBACK_TYPE_ALL_MATCHES
-                    || callbackType == CALLBACK_TYPE_FIRST_MATCH
-                    || callbackType == CALLBACK_TYPE_MATCH_LOST) {
-                return true;
-            }
-            return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);
-        }
-
-        /**
-         * Set scan result type for Bluetooth LE scan.
-         *
-         * @param scanResultType Type for scan result, could be either {@link
-         * ScanSettings#SCAN_RESULT_TYPE_FULL} or {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}.
-         * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
-         * @hide
-         */
-        @SystemApi
-        public Builder setScanResultType(int scanResultType) {
-            if (scanResultType < SCAN_RESULT_TYPE_FULL
-                    || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) {
-                throw new IllegalArgumentException(
-                        "invalid scanResultType - " + scanResultType);
-            }
-            mScanResultType = scanResultType;
-            return this;
-        }
-
-        /**
-         * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of
-         * scan results immediately. If &gt; 0, scan results are queued up and delivered after the
-         * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be
-         * delivered sooner if the internal buffers fill up.
-         *
-         * @param reportDelayMillis         how frequently scan results should be delivered in
-         *                                  milliseconds
-         * @throws IllegalArgumentException if {@code reportDelayMillis} &lt; 0
-         */
-        public Builder setReportDelay(long reportDelayMillis) {
-            if (reportDelayMillis < 0) {
-                throw new IllegalArgumentException("reportDelay must be > 0");
-            }
-            mReportDelayMillis = reportDelayMillis;
-            return this;
-        }
-
-        /**
-         * Set the number of matches for Bluetooth LE scan filters hardware match
-         *
-         * @param numOfMatches The num of matches can be one of
-         * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT}
-         * or {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or {@link
-         * ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
-         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
-         */
-        public Builder setNumOfMatches(int numOfMatches) {
-            if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
-                    || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
-                throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
-            }
-            mNumOfMatchesPerFilter = numOfMatches;
-            return this;
-        }
-
-        /**
-         * Set match mode for Bluetooth LE scan filters hardware match
-         *
-         * @param matchMode The match mode can be one of {@link ScanSettings#MATCH_MODE_AGGRESSIVE}
-         * or {@link ScanSettings#MATCH_MODE_STICKY}
-         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
-         */
-        public Builder setMatchMode(int matchMode) {
-            if (matchMode < MATCH_MODE_AGGRESSIVE
-                    || matchMode > MATCH_MODE_STICKY) {
-                throw new IllegalArgumentException("invalid matchMode " + matchMode);
-            }
-            mMatchMode = matchMode;
-            return this;
-        }
-
-        /**
-         * Set whether only legacy advertisments should be returned in scan results.
-         * Legacy advertisements include advertisements as specified by the
-         * Bluetooth core specification 4.2 and below. This is true by default
-         * for compatibility with older apps.
-         *
-         * @param legacy true if only legacy advertisements will be returned
-         */
-        public Builder setLegacy(boolean legacy) {
-            mLegacy = legacy;
-            return this;
-        }
-
-        /**
-         * Set the Physical Layer to use during this scan.
-         * This is used only if {@link ScanSettings.Builder#setLegacy}
-         * is set to false.
-         * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
-         * may be used to check whether LE Coded phy is supported by calling
-         * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
-         * Selecting an unsupported phy will result in failure to start scan.
-         *
-         * @param phy Can be one of {@link BluetoothDevice#PHY_LE_1M}, {@link
-         * BluetoothDevice#PHY_LE_CODED} or {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
-         */
-        public Builder setPhy(int phy) {
-            mPhy = phy;
-            return this;
-        }
-
-        /**
-         * Build {@link ScanSettings}.
-         */
-        public ScanSettings build() {
-            return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
-                    mReportDelayMillis, mMatchMode,
-                    mNumOfMatchesPerFilter, mLegacy, mPhy);
-        }
-    }
-}
diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java
deleted file mode 100644
index 18bad9c..0000000
--- a/core/java/android/bluetooth/le/TransportBlock.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * Wrapper for Transport Discovery Data Transport Blocks.
- * This class represents a Transport Block from a Transport Discovery Data.
- *
- * @see TransportDiscoveryData
- * @see AdvertiseData
- */
-public final class TransportBlock implements Parcelable {
-    private static final String TAG = "TransportBlock";
-    private final int mOrgId;
-    private final int mTdsFlags;
-    private final int mTransportDataLength;
-    private final byte[] mTransportData;
-
-    /**
-     * Creates an instance of TransportBlock from raw data.
-     *
-     * @param orgId the Organization ID
-     * @param tdsFlags the TDS flags
-     * @param transportDataLength the total length of the Transport Data
-     * @param transportData the Transport Data
-     */
-    public TransportBlock(int orgId, int tdsFlags, int transportDataLength,
-            @Nullable byte[] transportData) {
-        mOrgId = orgId;
-        mTdsFlags = tdsFlags;
-        mTransportDataLength = transportDataLength;
-        mTransportData = transportData;
-    }
-
-    private TransportBlock(Parcel in) {
-        mOrgId = in.readInt();
-        mTdsFlags = in.readInt();
-        mTransportDataLength = in.readInt();
-        if (mTransportDataLength > 0) {
-            mTransportData = new byte[mTransportDataLength];
-            in.readByteArray(mTransportData);
-        } else {
-            mTransportData = null;
-        }
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mOrgId);
-        dest.writeInt(mTdsFlags);
-        dest.writeInt(mTransportDataLength);
-        if (mTransportData != null) {
-            dest.writeByteArray(mTransportData);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        TransportBlock other = (TransportBlock) obj;
-        return Arrays.equals(toByteArray(), other.toByteArray());
-    }
-
-    public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() {
-        @Override
-        public TransportBlock createFromParcel(Parcel in) {
-            return new TransportBlock(in);
-        }
-
-        @Override
-        public TransportBlock[] newArray(int size) {
-            return new TransportBlock[size];
-        }
-    };
-
-    /**
-     * Gets the Organization ID of the Transport Block which corresponds to one of the
-     * the Bluetooth SIG Assigned Numbers.
-     */
-    public int getOrgId() {
-        return mOrgId;
-    }
-
-    /**
-     * Gets the TDS flags of the Transport Block which represents the role of the device and
-     * information about its state and supported features.
-     */
-    public int getTdsFlags() {
-        return mTdsFlags;
-    }
-
-    /**
-     * Gets the total number of octets in the Transport Data field in this Transport Block.
-     */
-    public int getTransportDataLength() {
-        return mTransportDataLength;
-    }
-
-    /**
-     * Gets the Transport Data of the Transport Block which contains organization-specific data.
-     */
-    @Nullable
-    public byte[] getTransportData() {
-        return mTransportData;
-    }
-
-    /**
-     * Converts this TransportBlock to byte array
-     *
-     * @return byte array representation of this Transport Block or null if the conversion failed
-     */
-    @Nullable
-    public byte[] toByteArray() {
-        try {
-            ByteBuffer buffer = ByteBuffer.allocate(totalBytes());
-            buffer.put((byte) mOrgId);
-            buffer.put((byte) mTdsFlags);
-            buffer.put((byte) mTransportDataLength);
-            if (mTransportData != null) {
-                buffer.put(mTransportData);
-            }
-            return buffer.array();
-        } catch (BufferOverflowException e) {
-            Log.e(TAG, "Error converting to byte array: " + e.toString());
-            return null;
-        }
-    }
-
-    /**
-     * @return total byte count of this TransportBlock
-     */
-    public int totalBytes() {
-        // 3 uint8 + byte[] length
-        int size = 3 + mTransportDataLength;
-        return size;
-    }
-}
diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java
deleted file mode 100644
index 2b52f19..0000000
--- a/core/java/android/bluetooth/le/TransportDiscoveryData.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.nio.BufferOverflowException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Wrapper for Transport Discovery Data AD Type.
- * This class contains the Transport Discovery Data AD Type Code as well as
- * a list of potential Transport Blocks.
- *
- * @see AdvertiseData
- */
-public final class TransportDiscoveryData implements Parcelable {
-    private static final String TAG = "TransportDiscoveryData";
-    private final int mTransportDataType;
-    private final List<TransportBlock> mTransportBlocks;
-
-    /**
-     * Creates a TransportDiscoveryData instance.
-     *
-     * @param transportDataType the Transport Discovery Data AD Type
-     * @param transportBlocks the list of Transport Blocks
-     */
-    public TransportDiscoveryData(int transportDataType,
-            @NonNull List<TransportBlock> transportBlocks) {
-        mTransportDataType = transportDataType;
-        mTransportBlocks = transportBlocks;
-    }
-
-    /**
-     * Creates a TransportDiscoveryData instance from byte arrays.
-     *
-     * Uses the transport discovery data bytes and parses them into an usable class.
-     *
-     * @param transportDiscoveryData the raw discovery data
-     */
-    public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) {
-        ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData);
-        mTransportBlocks = new ArrayList();
-        if (byteBuffer.remaining() > 0) {
-            mTransportDataType = byteBuffer.get();
-        } else {
-            mTransportDataType = -1;
-        }
-        try {
-            while (byteBuffer.remaining() > 0) {
-                int orgId = byteBuffer.get();
-                int tdsFlags = byteBuffer.get();
-                int transportDataLength = byteBuffer.get();
-                byte[] transportData = new byte[transportDataLength];
-                byteBuffer.get(transportData, 0, transportDataLength);
-                mTransportBlocks.add(new TransportBlock(orgId, tdsFlags,
-                        transportDataLength, transportData));
-            }
-        } catch (BufferUnderflowException e) {
-            Log.e(TAG, "Error while parsing data: " + e.toString());
-        }
-    }
-
-    private TransportDiscoveryData(Parcel in) {
-        mTransportDataType = in.readInt();
-        mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        TransportDiscoveryData other = (TransportDiscoveryData) obj;
-        return Arrays.equals(toByteArray(), other.toByteArray());
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mTransportDataType);
-        dest.writeTypedList(mTransportBlocks);
-    }
-
-    public static final @NonNull Creator<TransportDiscoveryData> CREATOR =
-            new Creator<TransportDiscoveryData>() {
-                @Override
-                public TransportDiscoveryData createFromParcel(Parcel in) {
-                    return new TransportDiscoveryData(in);
-                }
-
-                @Override
-                public TransportDiscoveryData[] newArray(int size) {
-                    return new TransportDiscoveryData[size];
-                }
-    };
-
-    /**
-     * Gets the transport data type.
-     */
-    public int getTransportDataType() {
-        return mTransportDataType;
-    }
-
-    /**
-     * @return the list of {@link TransportBlock} in this TransportDiscoveryData
-     *         or an empty list if there are no Transport Blocks
-     */
-    @NonNull
-    public List<TransportBlock> getTransportBlocks() {
-        if (mTransportBlocks == null) {
-            return Collections.emptyList();
-        }
-        return mTransportBlocks;
-    }
-
-    /**
-     * Converts this TransportDiscoveryData to byte array
-     *
-     * @return byte array representation of this Transport Discovery Data or null if the
-     *         conversion failed
-     */
-    @Nullable
-    public byte[] toByteArray() {
-        try {
-            ByteBuffer buffer = ByteBuffer.allocate(totalBytes());
-            buffer.put((byte) mTransportDataType);
-            for (TransportBlock transportBlock : getTransportBlocks()) {
-                buffer.put(transportBlock.toByteArray());
-            }
-            return buffer.array();
-        } catch (BufferOverflowException e) {
-            Log.e(TAG, "Error converting to byte array: " + e.toString());
-            return null;
-        }
-    }
-
-    /**
-     * @return total byte count of this TransportDataDiscovery
-     */
-    public int totalBytes() {
-        int size = 1; // Counting Transport Data Type here.
-        for (TransportBlock transportBlock : getTransportBlocks()) {
-            size += transportBlock.totalBytes();
-        }
-        return size;
-    }
-}
diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java
deleted file mode 100644
index 2592588..0000000
--- a/core/java/android/bluetooth/le/TruncatedFilter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-
-import java.util.List;
-
-/**
- * A special scan filter that lets the client decide how the scan record should be stored.
- *
- * @deprecated this is not used anywhere
- *
- * @hide
- */
-@Deprecated
-@SystemApi
-@SuppressLint("AndroidFrameworkBluetoothPermission")
-public final class TruncatedFilter {
-    private final ScanFilter mFilter;
-    private final List<ResultStorageDescriptor> mStorageDescriptors;
-
-    /**
-     * Constructor for {@link TruncatedFilter}.
-     *
-     * @param filter Scan filter of the truncated filter.
-     * @param storageDescriptors Describes how the scan should be stored.
-     */
-    public TruncatedFilter(ScanFilter filter, List<ResultStorageDescriptor> storageDescriptors) {
-        mFilter = filter;
-        mStorageDescriptors = storageDescriptors;
-    }
-
-    /**
-     * Returns the scan filter.
-     */
-    public ScanFilter getFilter() {
-        return mFilter;
-    }
-
-    /**
-     * Returns a list of descriptor for scan result storage.
-     */
-    public List<ResultStorageDescriptor> getStorageDescriptors() {
-        return mStorageDescriptors;
-    }
-
-
-}
diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html
deleted file mode 100644
index d9ca4f1..0000000
--- a/core/java/android/bluetooth/package.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<HTML>
-<BODY>
-<p>Provides classes that manage Bluetooth functionality, such as scanning for
-devices, connecting with devices, and managing data transfer between devices.
-The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.</p>
-
-<p>For more information about Classic Bluetooth, see the
-<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> guide.
-For more information about Bluetooth Low Energy, see the
-<a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">
-Bluetooth Low Energy</a> (BLE) guide.</p>
-{@more}
-
-<p>The Bluetooth APIs let applications:</p>
-<ul>
-  <li>Scan for other Bluetooth devices (including BLE devices).</li>
-  <li>Query the local Bluetooth adapter for paired Bluetooth devices.</li>
-  <li>Establish RFCOMM channels/sockets.</li>
-  <li>Connect to specified sockets on other devices.</li>
-  <li>Transfer data to and from other devices.</li>
-  <li>Communicate with BLE devices, such as proximity sensors, heart rate
-    monitors, fitness devices, and so on.</li>
-  <li>Act as a GATT client or a GATT server (BLE).</li>
-</ul>
-
-<p>
-To perform Bluetooth communication using these APIs, an application must
-declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some
-additional functionality, such as requesting device discovery,
-also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-permission.
-</p>
-
-<p class="note"><strong>Note:</strong>
-Not all Android-powered devices provide Bluetooth functionality.</p>
-
-</BODY>
-</HTML>
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index f0566b8..373a8d9 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -54,13 +54,13 @@
     private final @Nullable String mDeviceProfile;
 
     private final boolean mSelfManaged;
-    private boolean mNotifyOnDeviceNearby;
+    private final boolean mNotifyOnDeviceNearby;
     private final long mTimeApprovedMs;
     /**
      * A long value indicates the last time connected reported by selfManaged devices
      * Default value is Long.MAX_VALUE.
      */
-    private long mLastTimeConnectedMs;
+    private final long mLastTimeConnectedMs;
 
     /**
      * Creates a new Association.
@@ -160,22 +160,6 @@
         return mSelfManaged;
     }
 
-    /**
-     * Should only be used by the CompanionDeviceManagerService.
-     * @hide
-     */
-    public void setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
-        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
-    }
-
-    /**
-     * Should only be used by the CompanionDeviceManagerService.
-     * @hide
-     */
-    public void setLastTimeConnected(long lastTimeConnectedMs) {
-        mLastTimeConnectedMs = lastTimeConnectedMs;
-    }
-
     /** @hide */
     public boolean isNotifyOnDeviceNearby() {
         return mNotifyOnDeviceNearby;
@@ -330,4 +314,112 @@
             return new AssociationInfo(in);
         }
     };
+
+    /**
+     * Use this method to obtain a builder that you can use to create a copy of the
+     * given {@link AssociationInfo} with modified values of {@code mLastTimeConnected}
+     * or {@code mNotifyOnDeviceNearby}.
+     * <p>
+     *     Note that you <b>must</b> call either {@link Builder#setLastTimeConnected(long)
+     *     setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean)
+     *     setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}.
+     *
+     *     This is ensured statically at compile time.
+     *
+     * @hide
+     */
+    @NonNull
+    public static NonActionableBuilder builder(@NonNull AssociationInfo info) {
+        return new Builder(info);
+    }
+
+    /**
+     * @hide
+     */
+    public static final class Builder implements NonActionableBuilder {
+        @NonNull
+        private final AssociationInfo mOriginalInfo;
+        private boolean mNotifyOnDeviceNearby;
+        private long mLastTimeConnectedMs;
+
+        private Builder(@NonNull AssociationInfo info) {
+            mOriginalInfo = info;
+            mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
+            mLastTimeConnectedMs = info.mLastTimeConnectedMs;
+        }
+
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @Override
+        @NonNull
+        public Builder setLastTimeConnected(long lastTimeConnectedMs) {
+            if (lastTimeConnectedMs < 0) {
+                throw new IllegalArgumentException(
+                        "lastTimeConnectedMs must not be negative! (Given " + lastTimeConnectedMs
+                                + " )");
+            }
+            mLastTimeConnectedMs = lastTimeConnectedMs;
+            return this;
+        }
+
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @Override
+        @NonNull
+        public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
+            mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public AssociationInfo build() {
+            return new AssociationInfo(
+                    mOriginalInfo.mId,
+                    mOriginalInfo.mUserId,
+                    mOriginalInfo.mPackageName,
+                    mOriginalInfo.mDeviceMacAddress,
+                    mOriginalInfo.mDisplayName,
+                    mOriginalInfo.mDeviceProfile,
+                    mOriginalInfo.mSelfManaged,
+                    mNotifyOnDeviceNearby,
+                    mOriginalInfo.mTimeApprovedMs,
+                    mLastTimeConnectedMs
+            );
+        }
+    }
+
+    /**
+     * This interface is returned from the
+     * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point
+     * to indicate that this builder is not yet in a state that can produce a meaningful
+     * {@link AssociationInfo} object that is different from the one originally passed in.
+     *
+     * <p>
+     * Only by calling one of the setter methods is this builder turned into one where calling
+     * {@link Builder#build() build()} makes sense.
+     *
+     * @hide
+     */
+    public interface NonActionableBuilder {
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @NonNull
+        Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby);
+
+        /**
+         * Should only be used by the CompanionDeviceManagerService.
+         * @hide
+         */
+        @NonNull
+        Builder setLastTimeConnected(long lastTimeConnectedMs);
+    }
 }
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 12ced96..610b7ee 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -31,29 +31,71 @@
 import java.util.Objects;
 
 /**
- * Service to be implemented by apps that manage a companion device.
+ * A service that receives calls from the system when the associated companion device appears
+ * nearby or is connected, as well as when the device is no longer "present" or connected.
+ * See {@link #onDeviceAppeared(AssociationInfo)}/{@link #onDeviceDisappeared(AssociationInfo)}.
  *
- * System will keep this service bound whenever an associated device is nearby for Bluetooth
- * devices or companion app manages the connectivity and reports disappeared, ensuring app stays
- * alive
+ * <p>
+ * Additionally, the service will receive a call from the system, if and when the system needs to
+ * transfer data to the companion device.
+ * See {@link #dispatchMessage(int, int, byte[])}).
  *
- * An app must be {@link CompanionDeviceManager#associate associated} with at leas one device,
- * before it can take advantage of this service.
+ * <p>
+ * Companion applications must create a service that {@code extends}
+ * {@link CompanionDeviceService}, and declare it in their AndroidManifest.xml with the
+ * "android.permission.BIND_COMPANION_DEVICE_SERVICE" permission
+ * (see {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}),
+ * as well as add an intent filter for the "android.companion.CompanionDeviceService" action
+ * (see {@link #SERVICE_INTERFACE}).
  *
- * You must declare this service in your manifest with an
- * intent-filter action of {@link #SERVICE_INTERFACE} and
- * permission of {@link android.Manifest.permission#BIND_COMPANION_DEVICE_SERVICE}
+ * <p>
+ * Following is an example of such declaration:
+ * <pre>{@code
+ * <service
+ *        android:name=".CompanionService"
+ *        android:label="@string/service_name"
+ *        android:exported="true"
+ *        android:permission="android.permission.BIND_COMPANION_DEVICE_SERVICE">
+ *    <intent-filter>
+ *        <action android:name="android.companion.CompanionDeviceService" />
+ *    </intent-filter>
+ * </service>
+ * }</pre>
  *
- * <p>If you want to declare more than one of these services, you must declare the meta-data in the
- * service of your manifest with the corresponding name and value to true to indicate the
- * primary service.
- * Only the primary one will get the callback from
- * {@link #onDeviceAppeared(AssociationInfo associationInfo)}.</p>
+ * <p>
+ * If the companion application has requested observing device presence (see
+ * {@link CompanionDeviceManager#startObservingDevicePresence(String)}) the system will
+ * <a href="https://developer.android.com/guide/components/bound-services"> bind the service</a>
+ * when it detects the device nearby (for BLE devices) or when the device is connected
+ * (for Bluetooth devices).
  *
- * Example:
+ * <p>
+ * The system binding {@link CompanionDeviceService} elevates the priority of the process that
+ * the service is running in, and thus may prevent
+ * <a href="https://developer.android.com/topic/performance/memory-management#low-memory_killer">
+ * the Low-memory killer</a> from killing the process at expense of other processes with lower
+ * priority.
+ *
+ * <p>
+ * It is possible for an application to declare multiple {@link CompanionDeviceService}-s.
+ * In such case, the system will bind all declared services, but will deliver
+ * {@link #onDeviceAppeared(AssociationInfo)}, {@link #onDeviceDisappeared(AssociationInfo)} and
+ * {@link #dispatchMessage(int, int, byte[])} only to one "primary" services.
+ * Applications that declare multiple {@link CompanionDeviceService}-s should indicate the "primary"
+ * service using "android.companion.primary" tag.
+ * <pre>{@code
  * <meta-data
- *   android:name="primary"
- *   android:value="true" />
+ *       android:name="android.companion.primary"
+ *       android:value="true" />
+ * }</pre>
+ *
+ * <p>
+ * If the application declares multiple {@link CompanionDeviceService}-s, but does not indicate
+ * the "primary" one, the system will pick one of the declared services to use as "primary".
+ *
+ * <p>
+ * If the application declares multiple "primary" {@link CompanionDeviceService}-s, the system
+ * will pick single one of them to use as "primary".
  */
 public abstract class CompanionDeviceService extends Service {
 
diff --git a/core/java/android/companion/IOnAssociationsChangedListener.aidl b/core/java/android/companion/IOnAssociationsChangedListener.aidl
index e6794b7..d369456 100644
--- a/core/java/android/companion/IOnAssociationsChangedListener.aidl
+++ b/core/java/android/companion/IOnAssociationsChangedListener.aidl
@@ -20,5 +20,22 @@
 
 /** @hide */
 interface IOnAssociationsChangedListener {
-    oneway void onAssociationsChanged(in List<AssociationInfo> associations);
+
+    /*
+     * IMPORTANT: This method is intentionally NOT "oneway".
+     *
+     * The method is intentionally "blocking" to make sure that the clients of the
+     * addOnAssociationsChangedListener() API (@SystemAPI guarded by a "signature" permission) are
+     * able to prevent race conditions that may arise if their own clients (applications)
+     * effectively get notified about the changes before system services do.
+     *
+     * This is safe for 2 reasons:
+     *  1. The addOnAssociationsChangedListener() is only available to the system components
+     *     (guarded by a "signature" permission).
+     *     See android.permission.MANAGE_COMPANION_DEVICES.
+     *  2. On the Java side addOnAssociationsChangedListener() in CDM takes an Executor, and the
+     *     proxy implementation of onAssociationsChanged() simply "post" a Runnable to it.
+     *     See CompanionDeviceManager.OnAssociationsChangedListenerProxy class.
+     */
+    void onAssociationsChanged(in List<AssociationInfo> associations);
 }
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 82ad150..85855be 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -16,12 +16,14 @@
 
 package android.companion.virtual;
 
+import android.app.PendingIntent;
 import android.graphics.Point;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
 import android.hardware.input.VirtualMouseScrollEvent;
 import android.hardware.input.VirtualTouchEvent;
+import android.os.ResultReceiver;
 
 /**
  * Interface for a virtual device.
@@ -41,6 +43,7 @@
      * Closes the virtual device and frees all associated resources.
      */
     void close();
+
     void createVirtualKeyboard(
             int displayId,
             String inputDeviceName,
@@ -66,4 +69,10 @@
     boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
     boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
     boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
+
+    /**
+     * Launches a pending intent on the given display that is owned by this virtual device.
+     */
+    void launchPendingIntent(
+            int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 2dfa202..d80bee6 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -17,6 +17,7 @@
 package android.companion.virtual;
 
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceParams;
 
 /**
  * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
@@ -33,6 +34,10 @@
      *   that this belongs to the calling UID.
      * @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
      *   CDM. Virtual devices must have a corresponding association with CDM in order to be created.
+     * @param params The parameters for creating this virtual device. See {@link
+     *   VirtualDeviceManager.VirtualDeviceParams}.
      */
-    IVirtualDevice createVirtualDevice(in IBinder token, String packageName, int associationId);
+    IVirtualDevice createVirtualDevice(
+            in IBinder token, String packageName, int associationId,
+            in VirtualDeviceParams params);
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index bace45b..8ab6688 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -23,6 +23,8 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.Activity;
+import android.app.PendingIntent;
 import android.companion.AssociationInfo;
 import android.content.Context;
 import android.graphics.Point;
@@ -33,15 +35,19 @@
 import android.hardware.input.VirtualMouse;
 import android.hardware.input.VirtualTouchscreen;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.view.Surface;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.concurrent.Executor;
 
 /**
  * System level service for managing virtual devices.
@@ -100,11 +106,11 @@
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Nullable
-    public VirtualDevice createVirtualDevice(int associationId) {
+    public VirtualDevice createVirtualDevice(int associationId, VirtualDeviceParams params) {
         // TODO(b/194949534): Unhide this API
         try {
             IVirtualDevice virtualDevice = mService.createVirtualDevice(
-                    new Binder(), mContext.getPackageName(), associationId);
+                    new Binder(), mContext.getPackageName(), associationId, params);
             return new VirtualDevice(mContext, virtualDevice);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -129,6 +135,49 @@
         }
 
         /**
+         * Launches a given pending intent on the give display ID.
+         *
+         * @param displayId The display to launch the pending intent on. This display must be
+         *   created from this virtual device.
+         * @param pendingIntent The pending intent to be launched. If the intent is an activity
+         *   intent, the activity will be started on the virtual display using
+         *   {@link android.app.ActivityOptions#setLaunchDisplayId}. If the intent is a service or
+         *   broadcast intent, an attempt will be made to catch activities started as a result of
+         *   sending the pending intent and move them to the given display.
+         * @param executor The executor to run {@code launchCallback} on.
+         * @param launchCallback Callback that is called when the pending intent launching is
+         *   complete.
+         *
+         * @hide
+         */
+        public void launchPendingIntent(
+                int displayId,
+                @NonNull PendingIntent pendingIntent,
+                @NonNull Executor executor,
+                @NonNull LaunchCallback launchCallback) {
+            try {
+                mVirtualDevice.launchPendingIntent(
+                        displayId,
+                        pendingIntent,
+                        new ResultReceiver(new Handler(Looper.myLooper())) {
+                            @Override
+                            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                                super.onReceiveResult(resultCode, resultData);
+                                executor.execute(() -> {
+                                    if (resultCode == Activity.RESULT_OK) {
+                                        launchCallback.onLaunchSuccess();
+                                    } else {
+                                        launchCallback.onLaunchFailed();
+                                    }
+                                });
+                            }
+                        });
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
          * Creates a virtual display for this virtual device. All displays created on the same
          * device belongs to the same display group.
          *
@@ -273,6 +322,12 @@
             }
         }
 
+        /**
+         * Returns the display flags that should be added to a particular virtual display.
+         * Additional device-level flags from {@link
+         * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
+         * be added by DisplayManagerService.
+         */
         private int getVirtualDisplayFlags(@DisplayFlags int flags) {
             int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
             if ((flags & DISPLAY_FLAG_TRUSTED) != 0) {
@@ -293,4 +348,22 @@
             }
         }
     }
+
+    /**
+     * Callback for launching pending intents on the virtual device.
+     *
+     * @hide
+     */
+    // TODO(b/194949534): Unhide this API
+    public interface LaunchCallback {
+        /**
+         * Called when the pending intent launched successfully.
+         */
+        void onLaunchSuccess();
+
+        /**
+         * Called when the pending intent failed to launch.
+         */
+        void onLaunchFailed();
+    }
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/core/java/android/companion/virtual/VirtualDeviceParams.aidl
similarity index 89%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to core/java/android/companion/virtual/VirtualDeviceParams.aidl
index 6041460..9b3974a 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package android.companion.virtual;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable VirtualDeviceParams;
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
new file mode 100644
index 0000000..d61d474
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.util.ArraySet;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Params that can be configured when creating virtual devices.
+ *
+ * @hide
+ */
+// TODO(b/194949534): Unhide this API
+public final class VirtualDeviceParams implements Parcelable {
+
+    /** @hide */
+    @IntDef(prefix = "LOCK_STATE_",
+            value = {LOCK_STATE_ALWAYS_LOCKED, LOCK_STATE_ALWAYS_UNLOCKED})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    public @interface LockState {}
+
+    /**
+     * Indicates that the lock state of the virtual device should be always locked.
+     *
+     * @hide  // TODO(b/194949534): Unhide this API
+     */
+    public static final int LOCK_STATE_ALWAYS_LOCKED = 0;
+
+    /**
+     * Indicates that the lock state of the virtual device should be always unlocked.
+     *
+     * @hide  // TODO(b/194949534): Unhide this API
+     */
+    public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
+
+    private final int mLockState;
+    private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
+
+    private VirtualDeviceParams(
+            @LockState int lockState,
+            @NonNull Set<UserHandle> usersWithMatchingAccounts) {
+        mLockState = lockState;
+        mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
+    }
+
+    @SuppressWarnings("unchecked")
+    private VirtualDeviceParams(Parcel parcel) {
+        mLockState = parcel.readInt();
+        mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null);
+    }
+
+    /**
+     * Returns the lock state of the virtual device.
+     */
+    @LockState
+    public int getLockState() {
+        return mLockState;
+    }
+
+    /**
+     * Returns the user handles with matching managed accounts on the remote device to which
+     * this virtual device is streaming.
+     *
+     * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+     */
+    @NonNull
+    public Set<UserHandle> getUsersWithMatchingAccounts() {
+        return Collections.unmodifiableSet(mUsersWithMatchingAccounts);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mLockState);
+        dest.writeArraySet(mUsersWithMatchingAccounts);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof VirtualDeviceParams)) {
+            return false;
+        }
+        VirtualDeviceParams that = (VirtualDeviceParams) o;
+        return mLockState == that.mLockState && mUsersWithMatchingAccounts.equals(
+                that.mUsersWithMatchingAccounts);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLockState, mUsersWithMatchingAccounts);
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualDeviceParams("
+                + " mLockState=" + mLockState
+                + " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts
+                + ")";
+    }
+
+    public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
+            new Parcelable.Creator<VirtualDeviceParams>() {
+                public VirtualDeviceParams createFromParcel(Parcel in) {
+                    return new VirtualDeviceParams(in);
+                }
+
+                public VirtualDeviceParams[] newArray(int size) {
+                    return new VirtualDeviceParams[size];
+                }
+            };
+
+    /**
+     * Builder for {@link VirtualDeviceParams}.
+     */
+    public static final class Builder {
+
+        private @LockState int mLockState = LOCK_STATE_ALWAYS_LOCKED;
+        private Set<UserHandle> mUsersWithMatchingAccounts;
+
+        /**
+         * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
+         * is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
+         * The default is {@link #LOCK_STATE_ALWAYS_LOCKED}.
+         *
+         * @param lockState The lock state, either {@link #LOCK_STATE_ALWAYS_LOCKED} or
+         *   {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
+         */
+        @RequiresPermission(value = ADD_ALWAYS_UNLOCKED_DISPLAY, conditional = true)
+        @NonNull
+        public Builder setLockState(@LockState int lockState) {
+            mLockState = lockState;
+            return this;
+        }
+
+        /**
+         * Sets the user handles with matching managed accounts on the remote device to which
+         * this virtual device is streaming.
+         *
+         * @param usersWithMatchingAccounts A set of user handles with matching managed
+         *   accounts on the remote device this is streaming to.
+         * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+         */
+        public Builder setUsersWithMatchingAccounts(
+                @NonNull Set<UserHandle> usersWithMatchingAccounts) {
+            mUsersWithMatchingAccounts = usersWithMatchingAccounts;
+            return this;
+        }
+
+        /**
+         * Builds the {@link VirtualDeviceParams} instance.
+         */
+        @NonNull
+        public VirtualDeviceParams build() {
+            if (mUsersWithMatchingAccounts == null) {
+                mUsersWithMatchingAccounts = Collections.emptySet();
+            }
+            return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts);
+        }
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2df6f9a..0f24de6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3601,10 +3601,18 @@
      * Binds to a service in the given {@code user} in the same manner as
      * {@link #bindService(Intent, ServiceConnection, int)}.
      *
-     * <p>If the given {@code user} is in the same profile group and the target package is the
-     * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is
-     * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS}
-     * for interacting with other users.
+     * <p>Requires that one of the following conditions are met:
+     * <ul>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}</li>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is the same
+     *      package as the {@code service} (determined by its component's package) and the Android
+     *      version is at least {@link android.os.Build.VERSION_CODES#S}</li>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is in same
+     *      profile group as the given {@code user}</li>
+     *  <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} and is in same
+     *      profile group as the given {@code user} and is the same package as the {@code service}
+     *      </li>
+     * </ul>
      *
      * @param service Identifies the service to connect to.  The Intent must
      *      specify an explicit component name.
@@ -3626,8 +3634,9 @@
     @SuppressWarnings("unused")
     @RequiresPermission(anyOf = {
             android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
             android.Manifest.permission.INTERACT_ACROSS_PROFILES
-    })
+            }, conditional = true)
     public boolean bindServiceAsUser(
             @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags,
             @NonNull UserHandle user) {
@@ -3640,7 +3649,11 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES
+            }, conditional = true)
     @UnsupportedAppUsage(trackingBug = 136728678)
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             Handler handler, UserHandle user) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 74c326d..58a7d87 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2974,6 +2974,9 @@
      * Broadcast Action: A uid has been removed from the system.  The uid
      * number is stored in the extra data under {@link #EXTRA_UID}.
      *
+     * In certain instances, {@link #EXTRA_REPLACING} is set to true if the UID is not being
+     * fully removed.
+     *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      */
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 76b4e5c..9e9dd1e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1543,6 +1543,11 @@
     @Nullable
     private ArrayMap<String, String> mAppClassNamesByProcess;
 
+    /**
+     * Resource file providing the application's locales configuration.
+     */
+    private int localeConfigRes;
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -1660,6 +1665,10 @@
                 pw.println(prefix + "requestRawExternalStorageAccess="
                         + requestRawExternalStorageAccess);
             }
+            if (localeConfigRes != 0) {
+                pw.println(prefix + "localeConfigRes=0x"
+                        + Integer.toHexString(localeConfigRes));
+            }
         }
         pw.println(prefix + "createTimestamp=" + createTimestamp);
         super.dumpBack(pw, prefix);
@@ -1891,6 +1900,7 @@
         memtagMode = orig.memtagMode;
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
         requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
+        localeConfigRes = orig.localeConfigRes;
         createTimestamp = System.currentTimeMillis();
     }
 
@@ -1993,6 +2003,7 @@
                 dest.writeString(mAppClassNamesByProcess.valueAt(i));
             }
         }
+        dest.writeInt(localeConfigRes);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2088,6 +2099,7 @@
                 mAppClassNamesByProcess.put(source.readString(), source.readString());
             }
         }
+        localeConfigRes = source.readInt();
     }
 
     /**
@@ -2631,4 +2643,16 @@
         }
         return className;
     }
+
+    /** @hide */ public void setLocaleConfigRes(int value) { localeConfigRes = value; }
+
+    /**
+     * Return the resource id pointing to the resource file that provides the application's locales
+     * configuration.
+     *
+     * @hide
+     */
+    public int getLocaleConfigRes() {
+        return localeConfigRes;
+    }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a6d846b..d817f1e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2734,6 +2734,8 @@
      * API shipped in Android 11.
      * <li><code>202101</code>: corresponds to the features included in the Identity Credential
      * API shipped in Android 12.
+     * <li><code>202201</code>: corresponds to the features included in the Identity Credential
+     * API shipped in Android 13.
      * </ul>
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index d04c97c..a503d14 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -25,6 +25,9 @@
       "path": "cts/hostsidetests/packagemanager"
     },
     {
+      "path": "cts/hostsidetests/os/test_mappings/packagemanager"
+    },
+    {
       "path": "system/apex/tests"
     }
   ],
@@ -128,6 +131,23 @@
           "exclude-annotation": "org.junit.Ignore"
         }
       ]
+    },
+    {
+      "name": "CtsAppSecurityHostTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index fc9f1a5..e635e91 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -375,6 +375,8 @@
     ParsingPackage setResetEnabledSettingsOnAppDataCleared(
             boolean resetEnabledSettingsOnAppDataCleared);
 
+    ParsingPackage setLocaleConfigRes(int localeConfigRes);
+
     // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
     //  for moving to the next step
     @CallSuper
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index ddab207..fb42804 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -559,6 +559,8 @@
     private UUID mStorageUuid;
     private long mLongVersionCode;
 
+    private int mLocaleConfigRes;
+
     @VisibleForTesting
     public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
             @NonNull String path, @Nullable TypedArray manifestArray) {
@@ -1136,6 +1138,7 @@
         appInfo.setSplitResourcePaths(splitCodePaths);
         appInfo.setVersionCode(mLongVersionCode);
         appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
+        appInfo.setLocaleConfigRes(mLocaleConfigRes);
 
         return appInfo;
     }
@@ -1314,6 +1317,7 @@
         dest.writeInt(this.memtagMode);
         dest.writeInt(this.nativeHeapZeroInitialized);
         sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
+        dest.writeInt(this.mLocaleConfigRes);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1461,6 +1465,7 @@
         this.memtagMode = in.readInt();
         this.nativeHeapZeroInitialized = in.readInt();
         this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
+        this.mLocaleConfigRes = in.readInt();
         assignDerivedFields();
     }
 
@@ -2279,6 +2284,11 @@
         return nativeHeapZeroInitialized;
     }
 
+    @Override
+    public int getLocaleConfigRes() {
+        return mLocaleConfigRes;
+    }
+
     @Nullable
     @Override
     public Boolean hasRequestRawExternalStorageAccess() {
@@ -2936,4 +2946,10 @@
                 resetEnabledSettingsOnAppDataCleared);
         return this;
     }
+
+    @Override
+    public ParsingPackageImpl setLocaleConfigRes(int value) {
+        mLocaleConfigRes = value;
+        return this;
+    }
 }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index c8113ef..a5e98d6 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -343,4 +343,11 @@
      * @see R.styleable#AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared
      */
     boolean isResetEnabledSettingsOnAppDataCleared();
+
+    /**
+     * The resource ID used to provide the application's locales configuration.
+     *
+     * @see R.styleable#AndroidManifestApplication_localeConfig
+     */
+    int getLocaleConfigRes();
 }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index e02eb7c..795e7ef 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2339,6 +2339,7 @@
                 .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
                 .setDataExtractionRules(
                         resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa))
+                .setLocaleConfigRes(resId(R.styleable.AndroidManifestApplication_localeConfig, sa))
                 // Strings
                 .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
                 .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 08f5a8a..37cfb49 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -22,6 +22,8 @@
 import android.hardware.input.InputSensorInfo;
 import android.os.Build;
 
+import java.util.UUID;
+
 /**
  * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
  * the list of available sensors. For more information about Android sensors,
@@ -710,6 +712,20 @@
     public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
 
     /**
+     * A constant describing a head tracker sensor.
+     *
+     * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+     */
+    public static final int TYPE_HEAD_TRACKER = 37;
+
+    /**
+     * A constant string describing a head tracker sensor.
+     *
+     * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+     */
+    public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
+
+    /**
      * A constant describing all sensor types.
      */
 
@@ -829,6 +845,7 @@
             1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT
             6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
             1, // SENSOR_TYPE_HINGE_ANGLE
+            6, // SENSOR_TYPE_HEAD_TRACKER (discontinuity count is excluded)
     };
 
     /**
@@ -925,6 +942,7 @@
     @UnsupportedAppUsage
     private int     mFlags;
     private int     mId;
+    private UUID    mUuid;
 
     Sensor() {
     }
@@ -951,6 +969,8 @@
         this.mMaxDelay = sensorInfo.getMaxDelay();
         this.mFlags = sensorInfo.getFlags();
         this.mId = sensorInfo.getId();
+        // The UUID is never specified when creating a sensor from Input manager
+        this.mUuid = new UUID((long) this.mId, 0);
     }
 
     /**
@@ -1040,11 +1060,9 @@
     }
 
     /**
-     * Do not use.
-     *
-     * This method throws an UnsupportedOperationException.
-     *
-     * Use getId() if you want a unique ID.
+     * Reserved for system and audio servers.
+     * When called from an unauthorized context, the UUID will contain the
+     * sensor ID in the MSB and 0 in the LSB.
      *
      * @see getId
      *
@@ -1052,7 +1070,7 @@
      */
     @SystemApi
     public java.util.UUID getUuid() {
-        throw new UnsupportedOperationException();
+        return mUuid;
     }
 
     /**
@@ -1280,23 +1298,33 @@
             case TYPE_HINGE_ANGLE:
                 mStringType = STRING_TYPE_HINGE_ANGLE;
                 return true;
+            case TYPE_HEAD_TRACKER:
+                mStringType = STRING_TYPE_HEAD_TRACKER;
+                return true;
             default:
                 return false;
         }
     }
 
     /**
-     * Sets the ID associated with the sensor.
+     * Sets the UUID associated with the sensor.
      *
-     * The method name is misleading; while this ID is based on the UUID,
-     * we do not pass in the actual UUID.
+     * NOTE: to be used only by native bindings in SensorManager.
+     *
+     * @see #getUuid
+     */
+    private void setUuid(long msb, long lsb) {
+        mUuid = new UUID(msb, lsb);
+    }
+
+    /**
+     * Sets the ID associated with the sensor.
      *
      * NOTE: to be used only by native bindings in SensorManager.
      *
      * @see #getId
      */
-    private void setUuid(long msb, long lsb) {
-        // TODO(b/29547335): Rename this method to setId.
-        mId = (int) msb;
+    private void setId(int id) {
+        mId = id;
     }
 }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 232f234..c77c8cc 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -641,6 +641,41 @@
      *  <li> values[0]: Measured hinge angle between 0 and 360 degrees inclusive</li>
      * </ul>
      *
+     * <h4>{@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}:</h4>
+     *
+     * A sensor of this type measures the orientation of a user's head relative to an arbitrary
+     * reference frame, as well as the rate of rotation.
+     *
+     * Events produced by this sensor follow a special head-centric coordinate frame, where:
+     * <ul>
+     *  <li> The X axis crosses through the user's ears, with the positive X direction extending
+     *       out of the user's right ear</li>
+     *  <li> The Y axis crosses from the back of the user's head through their nose, with the
+     *       positive direction extending out of the nose, and the X/Y plane being nominally
+     *       parallel to the ground when the user is upright and looking straight ahead</li>
+     *  <li> The Z axis crosses from the neck through the top of the user's head, with the
+     *       positive direction extending out from the top of the head</li>
+     * </ul>
+     *
+     * Data is provided in Euler vector representation, which is a vector whose direction indicates
+     * the axis of rotation and magnitude indicates the angle to rotate around that axis, in
+     * radians.
+     *
+     * The first three elements provide the transform from the (arbitrary, possibly slowly drifting)
+     * reference frame to the head frame. The magnitude of this vector is in range [0, &pi;]
+     * radians, while the value of individual axes is in range [-&pi;, &pi;]. The next three
+     * elements provide the estimated rotational velocity of the user's head relative to itself, in
+     * radians per second.
+     *
+     * <ul>
+     *  <li> values[0] : X component of Euler vector representing rotation</li>
+     *  <li> values[1] : Y component of Euler vector representing rotation</li>
+     *  <li> values[2] : Z component of Euler vector representing rotation</li>
+     *  <li> values[3] : X component of Euler vector representing angular velocity</li>
+     *  <li> values[4] : Y component of Euler vector representing angular velocity</li>
+     *  <li> values[5] : Z component of Euler vector representing angular velocity</li>
+     * </ul>
+     *
      * @see GeomagneticField
      */
     public final float[] values;
diff --git a/core/java/android/hardware/SensorEventCallback.java b/core/java/android/hardware/SensorEventCallback.java
index bac212a..7b0092d 100644
--- a/core/java/android/hardware/SensorEventCallback.java
+++ b/core/java/android/hardware/SensorEventCallback.java
@@ -16,6 +16,8 @@
 
 package android.hardware;
 
+import android.annotation.NonNull;
+
 /**
  * Used for receiving sensor additional information frames.
  */
@@ -52,4 +54,21 @@
      * reported from sensor hardware.
      */
     public void onSensorAdditionalInfo(SensorAdditionalInfo info) {}
+
+    /**
+     * Called when the next {@link android.hardware.SensorEvent SensorEvent} to be delivered via the
+     * {@link #onSensorChanged(SensorEvent) onSensorChanged} method represents the first event after
+     * a discontinuity.
+     *
+     * The exact meaning of discontinuity depends on the sensor type. For {@link
+     * android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that the
+     * reference frame has suddenly and significantly changed.
+     *
+     * Note that this concept is either not relevant to or not supported by most sensor types,
+     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
+     * exception.
+     *
+     * @param sensor The {@link android.hardware.Sensor Sensor} which experienced the discontinuity.
+     */
+    public void onSensorDiscontinuity(@NonNull Sensor sensor) {}
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 3a8513b..32a5ee7 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -676,6 +676,7 @@
         private long mNativeSensorEventQueue;
         private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
         protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
+        protected final SparseIntArray mSensorDiscontinuityCounts = new SparseIntArray();
         private final CloseGuard mCloseGuard = CloseGuard.get();
         protected final SystemSensorManager mManager;
 
@@ -875,10 +876,21 @@
 
             // call onAccuracyChanged() only if the value changes
             final int accuracy = mSensorAccuracies.get(handle);
-            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+            if (t.accuracy >= 0 && accuracy != t.accuracy) {
                 mSensorAccuracies.put(handle, t.accuracy);
                 mListener.onAccuracyChanged(t.sensor, t.accuracy);
             }
+
+            // call onSensorDiscontinuity() if the discontinuity counter changed
+            if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER
+                    && mListener instanceof SensorEventCallback) {
+                final int lastCount = mSensorDiscontinuityCounts.get(handle);
+                final int curCount = Float.floatToIntBits(values[6]);
+                if (lastCount >= 0 && lastCount != curCount) {
+                    ((SensorEventCallback) mListener).onSensorDiscontinuity(t.sensor);
+                }
+            }
+
             mListener.onSensorChanged(t);
         }
 
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 6b5bec9..dc65bef 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.security.keystore.KeyProperties;
 import android.text.TextUtils;
 import android.util.Log;
@@ -666,8 +667,8 @@
     /**
      * A wrapper class for the cryptographic operations supported by BiometricPrompt.
      *
-     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
-     * {@link IdentityCredential}.
+     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+     * {@link IdentityCredential}, and {@link PresentationSession}.
      *
      * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
      * time-based. This is specified during key creation via the timeout parameter of the
@@ -697,10 +698,21 @@
             super(mac);
         }
 
+        /**
+         * Create from a {@link IdentityCredential} object.
+         *
+         * @param credential a {@link IdentityCredential} object.
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+         */
+        @Deprecated
         public CryptoObject(@NonNull IdentityCredential credential) {
             super(credential);
         }
 
+        public CryptoObject(@NonNull PresentationSession session) {
+            super(session);
+        }
+
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -728,10 +740,20 @@
         /**
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
          */
+        @Deprecated
         public @Nullable IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
+
+        /**
+         * Get {@link PresentationSession} object.
+         * @return {@link PresentationSession} object or null if this doesn't contain one.
+         */
+        public @Nullable PresentationSession getPresentationSession() {
+            return super.getPresentationSession();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 7648cf2..d415706 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.security.keystore2.AndroidKeyStoreProvider;
 
 import java.security.Signature;
@@ -27,8 +28,8 @@
 
 /**
  * A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
- * {@link IdentityCredential} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession} objects.
  * @hide
  */
 public class CryptoObject {
@@ -46,10 +47,21 @@
         mCrypto = mac;
     }
 
+    /**
+     * Create from a {@link IdentityCredential} object.
+     *
+     * @param credential a {@link IdentityCredential} object.
+     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+     */
+    @Deprecated
     public CryptoObject(@NonNull IdentityCredential credential) {
         mCrypto = credential;
     }
 
+    public CryptoObject(@NonNull PresentationSession session) {
+        mCrypto = session;
+    }
+
     /**
      * Get {@link Signature} object.
      * @return {@link Signature} object or null if this doesn't contain one.
@@ -77,12 +89,22 @@
     /**
      * Get {@link IdentityCredential} object.
      * @return {@link IdentityCredential} object or null if this doesn't contain one.
+     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
      */
+    @Deprecated
     public IdentityCredential getIdentityCredential() {
         return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
     }
 
     /**
+     * Get {@link PresentationSession} object.
+     * @return {@link PresentationSession} object or null if this doesn't contain one.
+     */
+    public PresentationSession getPresentationSession() {
+        return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
+    }
+
+    /**
      * @hide
      * @return the opId associated with this object or 0 if none
      */
@@ -91,6 +113,8 @@
             return 0;
         } else if (mCrypto instanceof IdentityCredential) {
             return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
+        } else if (mCrypto instanceof PresentationSession) {
+            return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
         }
         return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
     }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index de5c9ad..89ac8bf 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1132,41 +1132,48 @@
     }
 
     /**
-     * Sets the default display mode, according to the refresh rate and the resolution chosen by the
-     * user.
+     * Sets the global default {@link Display.Mode}.  The display mode includes preference for
+     * resolution and refresh rate. The mode change is applied globally, i.e. to all the connected
+     * displays. If the mode specified is not supported by a connected display, then no mode change
+     * occurs for that display.
      *
+     * @param mode The {@link Display.Mode} to set, which can include resolution and/or
+     * refresh-rate. It is created using {@link Display.Mode.Builder}.
+     *`
      * @hide
      */
     @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
-    public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) {
+    public void setGlobalUserPreferredDisplayMode(@NonNull Display.Mode mode) {
         // Create a new object containing default values for the unused fields like mode ID and
         // alternative refresh rates.
         Display.Mode preferredMode = new Display.Mode(mode.getPhysicalWidth(),
                 mode.getPhysicalHeight(), mode.getRefreshRate());
-        mGlobal.setUserPreferredDisplayMode(preferredMode);
+        mGlobal.setUserPreferredDisplayMode(Display.INVALID_DISPLAY, preferredMode);
     }
 
     /**
-     * Removes the user preferred display mode.
+     * Removes the global user preferred display mode.
+     * User preferred display mode is cleared for all the connected displays.
      *
      * @hide
      */
     @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
-    public void clearUserPreferredDisplayMode() {
-        mGlobal.setUserPreferredDisplayMode(null);
+    public void clearGlobalUserPreferredDisplayMode() {
+        mGlobal.setUserPreferredDisplayMode(Display.INVALID_DISPLAY, null);
     }
 
     /**
-     * Returns the user preferred display mode.
+     * Returns the global user preferred display mode.
+     * If no user preferred mode has been set, or it has been cleared, this method returns null.
      *
      * @hide
      */
     @TestApi
     @Nullable
-    public Display.Mode getUserPreferredDisplayMode() {
-        return mGlobal.getUserPreferredDisplayMode();
+    public Display.Mode getGlobalUserPreferredDisplayMode() {
+        return mGlobal.getUserPreferredDisplayMode(Display.INVALID_DISPLAY);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index bf6e665..1a7a63ae 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -896,9 +896,9 @@
      * Sets the default display mode, according to the refresh rate and the resolution chosen by the
      * user.
      */
-    public void setUserPreferredDisplayMode(Display.Mode mode) {
+    public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) {
         try {
-            mDm.setUserPreferredDisplayMode(mode);
+            mDm.setUserPreferredDisplayMode(displayId, mode);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -907,9 +907,9 @@
     /**
      * Returns the user preferred display mode.
      */
-    public Display.Mode getUserPreferredDisplayMode() {
+    public Display.Mode getUserPreferredDisplayMode(int displayId) {
         try {
-            return mDm.getUserPreferredDisplayMode();
+            return mDm.getUserPreferredDisplayMode(displayId);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index d38d388..35663af 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -166,8 +166,8 @@
 
     // Sets the user preferred display mode.
     // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission.
-    void setUserPreferredDisplayMode(in Mode mode);
-    Mode getUserPreferredDisplayMode();
+    void setUserPreferredDisplayMode(int displayId, in Mode mode);
+    Mode getUserPreferredDisplayMode(int displayId);
 
     // When enabled the app requested display resolution and refresh rate is always selected
     // in DisplayModeDirector regardless of user settings and policies for low brightness, low
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 56f8142..b970559 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -306,22 +306,21 @@
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "enrollment already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnEnrollCancelListener());
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "enrollment already canceled");
+            return;
         }
 
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enroll");
-                mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
-                        mContext.getOpPackageName(), disabledFeatures, previewSurface,
-                        debugConsent);
+                final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
+                        mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
+                        previewSurface, debugConsent);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or
@@ -359,21 +358,20 @@
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "enrollRemotely is already canceled.");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnEnrollCancelListener());
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "enrollRemotely is already canceled.");
+            return;
         }
 
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enrollRemotely");
-                mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
-                        mContext.getOpPackageName(), disabledFeatures);
+                final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken,
+                        mServiceReceiver, mContext.getOpPackageName(), disabledFeatures);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or
@@ -713,10 +711,10 @@
         }
     }
 
-    private void cancelEnrollment() {
+    private void cancelEnrollment(long requestId) {
         if (mService != null) {
             try {
-                mService.cancelEnrollment(mToken);
+                mService.cancelEnrollment(mToken, requestId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -1100,9 +1098,16 @@
     }
 
     private class OnEnrollCancelListener implements OnCancelListener {
+        private final long mAuthRequestId;
+
+        private OnEnrollCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelEnrollment();
+            Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId);
+            cancelEnrollment(mAuthRequestId);
         }
     }
 
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index e919824..989b001 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -76,15 +76,16 @@
     void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
 
     // Start face enrollment
-    void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
-            String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent);
+    long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+            String opPackageName, in int [] disabledFeatures,
+            in Surface previewSurface, boolean debugConsent);
 
     // Start remote face enrollment
-    void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+    long enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
             String opPackageName, in int [] disabledFeatures);
 
     // Cancel enrollment in progress
-    void cancelEnrollment(IBinder token);
+    void cancelEnrollment(IBinder token, long requestId);
 
     // Removes the specified face enrollment for the specified userId.
     void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe04e5d..7e070bc 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -58,6 +58,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.util.Slog;
 import android.view.Surface;
 
@@ -183,9 +184,16 @@
     }
 
     private class OnEnrollCancelListener implements OnCancelListener {
+        private final long mAuthRequestId;
+
+        private OnEnrollCancelListener(long id) {
+            mAuthRequestId = id;
+        }
+
         @Override
         public void onCancel() {
-            cancelEnrollment();
+            Slog.d(TAG, "Cancel fingerprint enrollment requested for: " + mAuthRequestId);
+            cancelEnrollment(mAuthRequestId);
         }
     }
 
@@ -264,10 +272,21 @@
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
          * @hide
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
          */
+        @Deprecated
         public IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
+
+        /**
+         * Get {@link PresentationSession} object.
+         * @return {@link PresentationSession} object or null if this doesn't contain one.
+         * @hide
+         */
+        public PresentationSession getPresentationSession() {
+            return super.getPresentationSession();
+        }
     }
 
     /**
@@ -646,20 +665,19 @@
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
 
-        if (cancel != null) {
-            if (cancel.isCanceled()) {
-                Slog.w(TAG, "enrollment already canceled");
-                return;
-            } else {
-                cancel.setOnCancelListener(new OnEnrollCancelListener());
-            }
+        if (cancel != null && cancel.isCanceled()) {
+            Slog.w(TAG, "enrollment already canceled");
+            return;
         }
 
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
-                mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
-                        mContext.getOpPackageName(), enrollReason);
+                final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
+                        mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+                if (cancel != null) {
+                    cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or try
@@ -1302,9 +1320,9 @@
         return allSensors.isEmpty() ? null : allSensors.get(0);
     }
 
-    private void cancelEnrollment() {
+    private void cancelEnrollment(long requestId) {
         if (mService != null) try {
-            mService.cancelEnrollment(mToken);
+            mService.cancelEnrollment(mToken, requestId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index ba1dc6d..cbff8b1 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -84,11 +84,11 @@
     void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
 
     // Start fingerprint enrollment
-    void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+    long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
             String opPackageName, int enrollReason);
 
     // Cancel enrollment in progress
-    void cancelEnrollment(IBinder token);
+    void cancelEnrollment(IBinder token, long requestId);
 
     // Any errors resulting from this call will be returned to the listener
     void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/hdmi/DeviceFeatures.java b/core/java/android/hardware/hdmi/DeviceFeatures.java
new file mode 100644
index 0000000..dc530ff
--- /dev/null
+++ b/core/java/android/hardware/hdmi/DeviceFeatures.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Immutable class that stores support status for features in the [Device Features] operand.
+ * Each feature may be supported, be not supported, or have an unknown support status.
+ *
+ * @hide
+ */
+public class DeviceFeatures {
+
+    @IntDef({
+            FEATURE_NOT_SUPPORTED,
+            FEATURE_SUPPORTED,
+            FEATURE_SUPPORT_UNKNOWN
+    })
+    public @interface FeatureSupportStatus {};
+
+    public static final int FEATURE_NOT_SUPPORTED = 0;
+    public static final int FEATURE_SUPPORTED = 1;
+    public static final int FEATURE_SUPPORT_UNKNOWN = 2;
+
+    /**
+     * Instance representing no knowledge of any feature's support.
+     */
+    @NonNull
+    public static final DeviceFeatures ALL_FEATURES_SUPPORT_UNKNOWN =
+            new Builder(FEATURE_SUPPORT_UNKNOWN).build();
+
+    /**
+     * Instance representing no support for any feature.
+     */
+    @NonNull
+    public static final DeviceFeatures NO_FEATURES_SUPPORTED =
+            new Builder(FEATURE_NOT_SUPPORTED).build();
+
+    @FeatureSupportStatus private final int mRecordTvScreenSupport;
+    @FeatureSupportStatus private final int mSetOsdStringSupport;
+    @FeatureSupportStatus private final int mDeckControlSupport;
+    @FeatureSupportStatus private final int mSetAudioRateSupport;
+    @FeatureSupportStatus private final int mArcTxSupport;
+    @FeatureSupportStatus private final int mArcRxSupport;
+    @FeatureSupportStatus private final int mSetAudioVolumeLevelSupport;
+
+    private DeviceFeatures(@NonNull Builder builder) {
+        this.mRecordTvScreenSupport = builder.mRecordTvScreenSupport;
+        this.mSetOsdStringSupport = builder.mOsdStringSupport;
+        this.mDeckControlSupport = builder.mDeckControlSupport;
+        this.mSetAudioRateSupport = builder.mSetAudioRateSupport;
+        this.mArcTxSupport = builder.mArcTxSupport;
+        this.mArcRxSupport = builder.mArcRxSupport;
+        this.mSetAudioVolumeLevelSupport = builder.mSetAudioVolumeLevelSupport;
+    }
+
+    /**
+     * Converts an instance to a builder.
+     */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    /**
+     * Constructs an instance from a [Device Features] operand.
+     *
+     * Bit 7 of [Device Features] is currently ignored. It indicates whether the operand spans more
+     * than one byte, but only the first byte contains information as of CEC 2.0.
+     *
+     * @param deviceFeaturesOperand The [Device Features] operand to parse.
+     * @return Instance representing the [Device Features] operand.
+     */
+    @NonNull
+    public static DeviceFeatures fromOperand(@NonNull byte[] deviceFeaturesOperand) {
+        Builder builder = new Builder(FEATURE_SUPPORT_UNKNOWN);
+
+        // Read feature support status from the bits of [Device Features]
+        if (deviceFeaturesOperand.length >= 1) {
+            byte b = deviceFeaturesOperand[0];
+            builder
+                    .setRecordTvScreenSupport(bitToFeatureSupportStatus(((b >> 6) & 1) == 1))
+                    .setSetOsdStringSupport(bitToFeatureSupportStatus(((b >> 5) & 1) == 1))
+                    .setDeckControlSupport(bitToFeatureSupportStatus(((b >> 4) & 1) == 1))
+                    .setSetAudioRateSupport(bitToFeatureSupportStatus(((b >> 3) & 1) == 1))
+                    .setArcTxSupport(bitToFeatureSupportStatus(((b >> 2) & 1) == 1))
+                    .setArcRxSupport(bitToFeatureSupportStatus(((b >> 1) & 1) == 1))
+                    .setSetAudioVolumeLevelSupport(bitToFeatureSupportStatus((b & 1) == 1));
+        }
+        return builder.build();
+    }
+
+    /**
+     * Returns the input that is not {@link #FEATURE_SUPPORT_UNKNOWN}. If neither is equal to
+     * {@link #FEATURE_SUPPORT_UNKNOWN}, returns the second input.
+     */
+    private static @FeatureSupportStatus int updateFeatureSupportStatus(
+            @FeatureSupportStatus int oldStatus, @FeatureSupportStatus int newStatus) {
+        if (newStatus == FEATURE_SUPPORT_UNKNOWN) {
+            return oldStatus;
+        } else {
+            return newStatus;
+        }
+    }
+
+    /**
+     * Returns the [Device Features] operand corresponding to this instance.
+     * {@link #FEATURE_SUPPORT_UNKNOWN} maps to 0, indicating no support.
+     *
+     * As of CEC 2.0, the returned byte array will always be of length 1.
+     */
+    @NonNull
+    public byte[] toOperand() {
+        byte result = 0;
+
+        if (mRecordTvScreenSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 6);
+        if (mSetOsdStringSupport == FEATURE_SUPPORTED) result = (byte) (1 << 5);
+        if (mDeckControlSupport == FEATURE_SUPPORTED) result = (byte) (1 << 4);
+        if (mSetAudioRateSupport == FEATURE_SUPPORTED) result = (byte) (1 << 3);
+        if (mArcTxSupport == FEATURE_SUPPORTED) result = (byte) (1 << 2);
+        if (mArcRxSupport == FEATURE_SUPPORTED) result = (byte) (1 << 1);
+        if (mSetAudioVolumeLevelSupport == FEATURE_SUPPORTED) result = (byte) 1;
+
+        return new byte[]{ result };
+    }
+
+    @FeatureSupportStatus
+    private static int bitToFeatureSupportStatus(boolean bit) {
+        return bit ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED;
+    }
+
+    /**
+     * Returns whether the device is a TV that supports <Record TV Screen>.
+     */
+    @FeatureSupportStatus
+    public int getRecordTvScreenSupport() {
+        return mRecordTvScreenSupport;
+    }
+
+    /**
+     * Returns whether the device is a TV that supports <Set OSD String>.
+     */
+    @FeatureSupportStatus
+    public int getSetOsdStringSupport() {
+        return mSetOsdStringSupport;
+    }
+
+    /**
+     * Returns whether the device supports being controlled by Deck Control.
+     */
+    @FeatureSupportStatus
+    public int getDeckControlSupport() {
+        return mDeckControlSupport;
+    }
+
+    /**
+     * Returns whether the device is a Source that supports <Set Audio Rate>.
+     */
+    @FeatureSupportStatus
+    public int getSetAudioRateSupport() {
+        return mSetAudioRateSupport;
+    }
+
+    /**
+     * Returns whether the device is a Sink that supports ARC Tx.
+     */
+    @FeatureSupportStatus
+    public int getArcTxSupport() {
+        return mArcTxSupport;
+    }
+
+    /**
+     * Returns whether the device is a Source that supports ARC Rx.
+     */
+    @FeatureSupportStatus
+    public int getArcRxSupport() {
+        return mArcRxSupport;
+    }
+
+    /**
+     * Returns whether the device supports <Set Audio Volume Level>.
+     */
+    @FeatureSupportStatus
+    public int getSetAudioVolumeLevelSupport() {
+        return mSetAudioVolumeLevelSupport;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof DeviceFeatures)) {
+            return false;
+        }
+
+        DeviceFeatures other = (DeviceFeatures) obj;
+        return mRecordTvScreenSupport == other.mRecordTvScreenSupport
+                && mSetOsdStringSupport == other.mSetOsdStringSupport
+                && mDeckControlSupport == other.mDeckControlSupport
+                && mSetAudioRateSupport == other.mSetAudioRateSupport
+                && mArcTxSupport == other.mArcTxSupport
+                && mArcRxSupport == other.mArcRxSupport
+                && mSetAudioVolumeLevelSupport == other.mSetAudioVolumeLevelSupport;
+    }
+
+    @Override
+    public int hashCode() {
+        return java.util.Objects.hash(
+                mRecordTvScreenSupport,
+                mSetOsdStringSupport,
+                mDeckControlSupport,
+                mSetAudioRateSupport,
+                mArcTxSupport,
+                mArcRxSupport,
+                mSetAudioVolumeLevelSupport
+        );
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("Device features: ");
+        s.append("record_tv_screen: ")
+                .append(featureSupportStatusToString(mRecordTvScreenSupport)).append(" ");
+        s.append("set_osd_string: ")
+                .append(featureSupportStatusToString(mSetOsdStringSupport)).append(" ");
+        s.append("deck_control: ")
+                .append(featureSupportStatusToString(mDeckControlSupport)).append(" ");
+        s.append("set_audio_rate: ")
+                .append(featureSupportStatusToString(mSetAudioRateSupport)).append(" ");
+        s.append("arc_tx: ")
+                .append(featureSupportStatusToString(mArcTxSupport)).append(" ");
+        s.append("arc_rx: ")
+                .append(featureSupportStatusToString(mArcRxSupport)).append(" ");
+        s.append("set_audio_volume_level: ")
+                .append(featureSupportStatusToString(mSetAudioVolumeLevelSupport)).append(" ");
+        return s.toString();
+    }
+
+    @NonNull
+    private static String featureSupportStatusToString(@FeatureSupportStatus int status) {
+        switch (status) {
+            case FEATURE_SUPPORTED:
+                return "Y";
+            case FEATURE_NOT_SUPPORTED:
+                return "N";
+            case FEATURE_SUPPORT_UNKNOWN:
+            default:
+                return "?";
+        }
+    }
+
+    /**
+     * Builder for {@link DeviceFeatures} instances.
+     */
+    public static final class Builder {
+        @FeatureSupportStatus private int mRecordTvScreenSupport;
+        @FeatureSupportStatus private int mOsdStringSupport;
+        @FeatureSupportStatus private int mDeckControlSupport;
+        @FeatureSupportStatus private int mSetAudioRateSupport;
+        @FeatureSupportStatus private int mArcTxSupport;
+        @FeatureSupportStatus private int mArcRxSupport;
+        @FeatureSupportStatus private int mSetAudioVolumeLevelSupport;
+
+        private Builder(@FeatureSupportStatus int defaultFeatureSupportStatus) {
+            mRecordTvScreenSupport = defaultFeatureSupportStatus;
+            mOsdStringSupport = defaultFeatureSupportStatus;
+            mDeckControlSupport = defaultFeatureSupportStatus;
+            mSetAudioRateSupport = defaultFeatureSupportStatus;
+            mArcTxSupport = defaultFeatureSupportStatus;
+            mArcRxSupport = defaultFeatureSupportStatus;
+            mSetAudioVolumeLevelSupport = defaultFeatureSupportStatus;
+        }
+
+        private Builder(DeviceFeatures info) {
+            mRecordTvScreenSupport = info.getRecordTvScreenSupport();
+            mOsdStringSupport = info.getSetOsdStringSupport();
+            mDeckControlSupport = info.getDeckControlSupport();
+            mSetAudioRateSupport = info.getSetAudioRateSupport();
+            mArcTxSupport = info.getArcTxSupport();
+            mArcRxSupport = info.getArcRxSupport();
+            mSetAudioVolumeLevelSupport = info.getSetAudioVolumeLevelSupport();
+        }
+
+        /**
+         * Creates a new {@link DeviceFeatures} object.
+         */
+        public DeviceFeatures build() {
+            return new DeviceFeatures(this);
+        }
+
+        /**
+         * Sets the value for {@link #getRecordTvScreenSupport()}.
+         */
+        @NonNull
+        public Builder setRecordTvScreenSupport(@FeatureSupportStatus int recordTvScreenSupport) {
+            mRecordTvScreenSupport = recordTvScreenSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getSetOsdStringSupport()}.
+         */
+        @NonNull
+        public Builder setSetOsdStringSupport(@FeatureSupportStatus int setOsdStringSupport) {
+            mOsdStringSupport = setOsdStringSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeckControlSupport()}.
+         */
+        @NonNull
+        public Builder setDeckControlSupport(@FeatureSupportStatus int deckControlSupport) {
+            mDeckControlSupport = deckControlSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getSetAudioRateSupport()}.
+         */
+        @NonNull
+        public Builder setSetAudioRateSupport(@FeatureSupportStatus int setAudioRateSupport) {
+            mSetAudioRateSupport = setAudioRateSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getArcTxSupport()}.
+         */
+        @NonNull
+        public Builder setArcTxSupport(@FeatureSupportStatus int arcTxSupport) {
+            mArcTxSupport = arcTxSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getArcRxSupport()}.
+         */
+        @NonNull
+        public Builder setArcRxSupport(@FeatureSupportStatus int arcRxSupport) {
+            mArcRxSupport = arcRxSupport;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getSetAudioRateSupport()}.
+         */
+        @NonNull
+        public Builder setSetAudioVolumeLevelSupport(
+                @FeatureSupportStatus int setAudioVolumeLevelSupport) {
+            mSetAudioVolumeLevelSupport = setAudioVolumeLevelSupport;
+            return this;
+        }
+
+        /**
+         * Updates all fields with those in a 'new' instance of {@link DeviceFeatures}.
+         * All fields are replaced with those in the new instance, except when the field is
+         * {@link #FEATURE_SUPPORT_UNKNOWN} in the new instance.
+         */
+        @NonNull
+        public Builder update(DeviceFeatures newDeviceFeatures) {
+            mRecordTvScreenSupport = updateFeatureSupportStatus(mRecordTvScreenSupport,
+                    newDeviceFeatures.getRecordTvScreenSupport());
+            mOsdStringSupport = updateFeatureSupportStatus(mOsdStringSupport,
+                    newDeviceFeatures.getSetOsdStringSupport());
+            mDeckControlSupport = updateFeatureSupportStatus(mDeckControlSupport,
+                    newDeviceFeatures.getDeckControlSupport());
+            mSetAudioRateSupport = updateFeatureSupportStatus(mSetAudioRateSupport,
+                    newDeviceFeatures.getSetAudioRateSupport());
+            mArcTxSupport = updateFeatureSupportStatus(mArcTxSupport,
+                    newDeviceFeatures.getArcTxSupport());
+            mArcRxSupport = updateFeatureSupportStatus(mArcRxSupport,
+                    newDeviceFeatures.getArcRxSupport());
+            mSetAudioVolumeLevelSupport = updateFeatureSupportStatus(mSetAudioVolumeLevelSupport,
+                    newDeviceFeatures.getSetAudioVolumeLevelSupport());
+            return this;
+        }
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
index c0b177d..e3ea4e2 100644
--- a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java
@@ -68,6 +68,9 @@
      */
     public static final int ADDR_INTERNAL = 0;
 
+    /** Invalid or uninitialized logical address */
+    public static final int ADDR_INVALID = -1;
+
     /**
      * Physical address used to indicate the source comes from internal device. The physical address
      * of TV(0) is used.
@@ -83,7 +86,15 @@
     /** Invalid device ID */
     public static final int ID_INVALID = 0xFFFF;
 
-    /** Device info used to indicate an inactivated device. */
+    /** Unknown vendor ID */
+    public static final int VENDOR_ID_UNKNOWN = 0xFFFFFF;
+
+    /**
+     * Instance that represents an inactive device.
+     * Can be passed to an input change listener to indicate that the active source
+     * yielded its status, allowing the listener to take an appropriate action such as
+     * switching to another input.
+     */
     public static final HdmiDeviceInfo INACTIVE_DEVICE = new HdmiDeviceInfo();
 
     private static final int HDMI_DEVICE_TYPE_CEC = 0;
@@ -109,10 +120,11 @@
     private final int mLogicalAddress;
     private final int mDeviceType;
     @HdmiCecVersion
-    private final int mHdmiCecVersion;
+    private final int mCecVersion;
     private final int mVendorId;
     private final String mDisplayName;
     private final int mDevicePowerStatus;
+    private final DeviceFeatures mDeviceFeatures;
 
     // MHL only parameters.
     private final int mDeviceId;
@@ -137,14 +149,22 @@
                             int powerStatus = source.readInt();
                             String displayName = source.readString();
                             int cecVersion = source.readInt();
-                            return new HdmiDeviceInfo(logicalAddress, physicalAddress, portId,
-                                    deviceType, vendorId, displayName, powerStatus, cecVersion);
+                            return cecDeviceBuilder()
+                                    .setLogicalAddress(logicalAddress)
+                                    .setPhysicalAddress(physicalAddress)
+                                    .setPortId(portId)
+                                    .setDeviceType(deviceType)
+                                    .setVendorId(vendorId)
+                                    .setDisplayName(displayName)
+                                    .setDevicePowerStatus(powerStatus)
+                                    .setCecVersion(cecVersion)
+                                    .build();
                         case HDMI_DEVICE_TYPE_MHL:
                             int deviceId = source.readInt();
                             int adopterId = source.readInt();
-                            return new HdmiDeviceInfo(physicalAddress, portId, adopterId, deviceId);
+                            return mhlDevice(physicalAddress, portId, adopterId, deviceId);
                         case HDMI_DEVICE_TYPE_HARDWARE:
-                            return new HdmiDeviceInfo(physicalAddress, portId);
+                            return hardwarePort(physicalAddress, portId);
                         case HDMI_DEVICE_TYPE_INACTIVE:
                             return HdmiDeviceInfo.INACTIVE_DEVICE;
                         default:
@@ -159,97 +179,82 @@
             };
 
     /**
-     * Constructor. Used to initialize the instance for CEC device.
+     * Constructor. Initializes the instance representing an inactive device.
+     * Can be passed to an input change listener to indicate that the active source
+     * yielded its status, allowing the listener to take an appropriate action such as
+     * switching to another input.
      *
-     * @param logicalAddress logical address of HDMI-CEC device
-     * @param physicalAddress physical address of HDMI-CEC device
-     * @param portId HDMI port ID (1 for HDMI1)
-     * @param deviceType type of device
-     * @param vendorId vendor id of device. Used for vendor specific command.
-     * @param displayName name of device
-     * @param powerStatus device power status
-     * @hide
+     * @deprecated Use {@link #INACTIVE_DEVICE} instead.
      */
-    public HdmiDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
-            int vendorId, String displayName, int powerStatus, int hdmiCecVersion) {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_CEC;
-        mPhysicalAddress = physicalAddress;
-        mPortId = portId;
+    @Deprecated
+    public HdmiDeviceInfo() {
+        mHdmiDeviceType = HDMI_DEVICE_TYPE_INACTIVE;
+        mPhysicalAddress = PATH_INVALID;
+        mId = ID_INVALID;
 
-        mId = idForCecDevice(logicalAddress);
-        mLogicalAddress = logicalAddress;
-        mDeviceType = deviceType;
-        mHdmiCecVersion = hdmiCecVersion;
-        mVendorId = vendorId;
-        mDevicePowerStatus = powerStatus;
-        mDisplayName = displayName;
-
-        mDeviceId = -1;
-        mAdopterId = -1;
-    }
-
-    /**
-     * Constructor. Used to initialize the instance for CEC device.
-     *
-     * @param logicalAddress logical address of HDMI-CEC device
-     * @param physicalAddress physical address of HDMI-CEC device
-     * @param portId HDMI port ID (1 for HDMI1)
-     * @param deviceType type of device
-     * @param vendorId vendor id of device. Used for vendor specific command.
-     * @param displayName name of device
-     * @param powerStatus device power status
-     * @hide
-     */
-    public HdmiDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
-            int vendorId, String displayName, int powerStatus) {
-        this(logicalAddress, physicalAddress, portId, deviceType,
-                vendorId, displayName, powerStatus, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-    }
-
-    /**
-     * Constructor. Used to initialize the instance for CEC device.
-     *
-     * @param logicalAddress  logical address of HDMI-CEC device
-     * @param physicalAddress physical address of HDMI-CEC device
-     * @param portId          HDMI port ID (1 for HDMI1)
-     * @param deviceType      type of device
-     * @param vendorId        vendor id of device. Used for vendor specific command.
-     * @param displayName     name of device
-     * @hide
-     */
-    public HdmiDeviceInfo(int logicalAddress, int physicalAddress, int portId, int deviceType,
-            int vendorId, String displayName) {
-        this(logicalAddress, physicalAddress, portId, deviceType,
-                vendorId, displayName, HdmiControlManager.POWER_STATUS_UNKNOWN,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-    }
-
-    /**
-     * Constructor. Used to initialize the instance for device representing hardware port.
-     *
-     * @param physicalAddress physical address of the port
-     * @param portId HDMI port ID (1 for HDMI1)
-     * @hide
-     */
-    public HdmiDeviceInfo(int physicalAddress, int portId) {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_HARDWARE;
-        mPhysicalAddress = physicalAddress;
-        mPortId = portId;
-
-        mId = idForHardware(portId);
-        mLogicalAddress = -1;
-        mDeviceType = DEVICE_RESERVED;
-        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
-        mVendorId = 0;
+        mLogicalAddress = ADDR_INVALID;
+        mDeviceType = DEVICE_INACTIVE;
+        mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+        mPortId = PORT_INVALID;
         mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
-        mDisplayName = "HDMI" + portId;
+        mDisplayName = "Inactive";
+        mVendorId = 0;
+        mDeviceFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN;
 
         mDeviceId = -1;
         mAdopterId = -1;
     }
 
     /**
-     * Constructor. Used to initialize the instance for MHL device.
+     * Converts an instance to a builder.
+     *
+     * @hide
+     */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    private HdmiDeviceInfo(Builder builder) {
+        this.mHdmiDeviceType = builder.mHdmiDeviceType;
+        this.mPhysicalAddress = builder.mPhysicalAddress;
+        this.mPortId = builder.mPortId;
+        this.mLogicalAddress = builder.mLogicalAddress;
+        this.mDeviceType = builder.mDeviceType;
+        this.mCecVersion = builder.mCecVersion;
+        this.mVendorId = builder.mVendorId;
+        this.mDisplayName = builder.mDisplayName;
+        this.mDevicePowerStatus = builder.mDevicePowerStatus;
+        this.mDeviceFeatures = builder.mDeviceFeatures;
+        this.mDeviceId = builder.mDeviceId;
+        this.mAdopterId = builder.mAdopterId;
+
+        switch (mHdmiDeviceType) {
+            case HDMI_DEVICE_TYPE_MHL:
+                this.mId = idForMhlDevice(mPortId);
+                break;
+            case HDMI_DEVICE_TYPE_HARDWARE:
+                this.mId = idForHardware(mPortId);
+                break;
+            case HDMI_DEVICE_TYPE_CEC:
+                this.mId = idForCecDevice(mLogicalAddress);
+                break;
+            case HDMI_DEVICE_TYPE_INACTIVE:
+            default:
+                this.mId = ID_INVALID;
+        }
+    }
+
+    /**
+     * Creates a Builder for an {@link HdmiDeviceInfo} representing a CEC device.
+     *
+     * @hide
+     */
+    public static Builder cecDeviceBuilder() {
+        return new Builder(HDMI_DEVICE_TYPE_CEC);
+    }
+
+    /**
+     * Creates an {@link HdmiDeviceInfo} representing an MHL device.
      *
      * @param physicalAddress physical address of HDMI device
      * @param portId portId HDMI port ID (1 for HDMI1)
@@ -257,44 +262,32 @@
      * @param deviceId device id of MHL
      * @hide
      */
-    public HdmiDeviceInfo(int physicalAddress, int portId, int adopterId, int deviceId) {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_MHL;
-        mPhysicalAddress = physicalAddress;
-        mPortId = portId;
-
-        mId = idForMhlDevice(portId);
-        mLogicalAddress = -1;
-        mDeviceType = DEVICE_RESERVED;
-        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
-        mVendorId = 0;
-        mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
-        mDisplayName = "Mobile";
-
-        mDeviceId = adopterId;
-        mAdopterId = deviceId;
+    public static HdmiDeviceInfo mhlDevice(
+            int physicalAddress, int portId, int adopterId, int deviceId) {
+        return new Builder(HDMI_DEVICE_TYPE_MHL)
+                .setPhysicalAddress(physicalAddress)
+                .setPortId(portId)
+                .setVendorId(0)
+                .setDisplayName("Mobile")
+                .setDeviceId(adopterId)
+                .setAdopterId(deviceId)
+                .build();
     }
 
     /**
-     * Constructor. Used to initialize the instance representing an inactivated device.
-     * Can be passed input change listener to indicate the active source yielded
-     * its status, hence the listener should take an appropriate action such as
-     * switching to other input.
+     * Creates an {@link HdmiDeviceInfo} representing a hardware port.
+     *
+     * @param physicalAddress physical address of the port
+     * @param portId HDMI port ID (1 for HDMI1)
+     * @hide
      */
-    public HdmiDeviceInfo() {
-        mHdmiDeviceType = HDMI_DEVICE_TYPE_INACTIVE;
-        mPhysicalAddress = PATH_INVALID;
-        mId = ID_INVALID;
-
-        mLogicalAddress = -1;
-        mDeviceType = DEVICE_INACTIVE;
-        mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
-        mPortId = PORT_INVALID;
-        mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
-        mDisplayName = "Inactive";
-        mVendorId = 0;
-
-        mDeviceId = -1;
-        mAdopterId = -1;
+    public static HdmiDeviceInfo hardwarePort(int physicalAddress, int portId) {
+        return new Builder(HDMI_DEVICE_TYPE_HARDWARE)
+                .setPhysicalAddress(physicalAddress)
+                .setPortId(portId)
+                .setVendorId(0)
+                .setDisplayName("HDMI" + portId)
+                .build();
     }
 
     /**
@@ -305,6 +298,15 @@
     }
 
     /**
+     * Returns the CEC features that this device supports.
+     *
+     * @hide
+     */
+    public DeviceFeatures getDeviceFeatures() {
+        return mDeviceFeatures;
+    }
+
+    /**
      * Returns the id to be used for CEC device.
      *
      * @param address logical address of CEC device
@@ -372,7 +374,7 @@
      */
     @HdmiCecVersion
     public int getCecVersion() {
-        return mHdmiCecVersion;
+        return mCecVersion;
     }
 
     /**
@@ -485,7 +487,7 @@
                 dest.writeInt(mVendorId);
                 dest.writeInt(mDevicePowerStatus);
                 dest.writeString(mDisplayName);
-                dest.writeInt(mHdmiCecVersion);
+                dest.writeInt(mCecVersion);
                 break;
             case HDMI_DEVICE_TYPE_MHL:
                 dest.writeInt(mDeviceId);
@@ -508,7 +510,7 @@
                 s.append("logical_address: ").append(String.format("0x%02X", mLogicalAddress));
                 s.append(" ");
                 s.append("device_type: ").append(mDeviceType).append(" ");
-                s.append("cec_version: ").append(mHdmiCecVersion).append(" ");
+                s.append("cec_version: ").append(mCecVersion).append(" ");
                 s.append("vendor_id: ").append(mVendorId).append(" ");
                 s.append("display_name: ").append(mDisplayName).append(" ");
                 s.append("power_status: ").append(mDevicePowerStatus).append(" ");
@@ -531,6 +533,11 @@
         s.append("physical_address: ").append(String.format("0x%04X", mPhysicalAddress));
         s.append(" ");
         s.append("port_id: ").append(mPortId);
+
+        if (mHdmiDeviceType == HDMI_DEVICE_TYPE_CEC) {
+            s.append("\n  ").append(mDeviceFeatures.toString());
+        }
+
         return s.toString();
     }
 
@@ -546,7 +553,7 @@
                 && mPortId == other.mPortId
                 && mLogicalAddress == other.mLogicalAddress
                 && mDeviceType == other.mDeviceType
-                && mHdmiCecVersion == other.mHdmiCecVersion
+                && mCecVersion == other.mCecVersion
                 && mVendorId == other.mVendorId
                 && mDevicePowerStatus == other.mDevicePowerStatus
                 && mDisplayName.equals(other.mDisplayName)
@@ -562,11 +569,180 @@
                 mPortId,
                 mLogicalAddress,
                 mDeviceType,
-                mHdmiCecVersion,
+                mCecVersion,
                 mVendorId,
                 mDevicePowerStatus,
                 mDisplayName,
                 mDeviceId,
                 mAdopterId);
     }
+
+    /**
+     * Builder for {@link HdmiDeviceInfo} instances.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        // Required parameters
+        private final int mHdmiDeviceType;
+
+        // Common parameters
+        private int mPhysicalAddress = PATH_INVALID;
+        private int mPortId = PORT_INVALID;
+
+        // CEC parameters
+        private int mLogicalAddress = ADDR_INVALID;
+        private int mDeviceType = DEVICE_RESERVED;
+        @HdmiCecVersion
+        private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+        private int mVendorId = VENDOR_ID_UNKNOWN;
+        private String mDisplayName = "";
+        private int mDevicePowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+        private DeviceFeatures mDeviceFeatures;
+
+        // MHL parameters
+        private int mDeviceId = -1;
+        private int mAdopterId = -1;
+
+        private Builder(int hdmiDeviceType) {
+            mHdmiDeviceType = hdmiDeviceType;
+            if (hdmiDeviceType == HDMI_DEVICE_TYPE_CEC) {
+                mDeviceFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN;
+            } else {
+                mDeviceFeatures = DeviceFeatures.NO_FEATURES_SUPPORTED;
+            }
+        }
+
+        private Builder(@NonNull HdmiDeviceInfo hdmiDeviceInfo) {
+            mHdmiDeviceType = hdmiDeviceInfo.mHdmiDeviceType;
+            mPhysicalAddress = hdmiDeviceInfo.mPhysicalAddress;
+            mPortId = hdmiDeviceInfo.mPortId;
+            mLogicalAddress = hdmiDeviceInfo.mLogicalAddress;
+            mDeviceType = hdmiDeviceInfo.mDeviceType;
+            mCecVersion = hdmiDeviceInfo.mCecVersion;
+            mVendorId = hdmiDeviceInfo.mVendorId;
+            mDisplayName = hdmiDeviceInfo.mDisplayName;
+            mDevicePowerStatus = hdmiDeviceInfo.mDevicePowerStatus;
+            mDeviceId = hdmiDeviceInfo.mDeviceId;
+            mAdopterId = hdmiDeviceInfo.mAdopterId;
+            mDeviceFeatures = hdmiDeviceInfo.mDeviceFeatures;
+        }
+
+        /**
+         * Create a new {@link HdmiDeviceInfo} object.
+         */
+        @NonNull
+        public HdmiDeviceInfo build() {
+            return new HdmiDeviceInfo(this);
+        }
+
+        /**
+         * Sets the value for {@link #getPhysicalAddress()}.
+         */
+        @NonNull
+        public Builder setPhysicalAddress(int physicalAddress) {
+            mPhysicalAddress = physicalAddress;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getPortId()}.
+         */
+        @NonNull
+        public Builder setPortId(int portId) {
+            mPortId = portId;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getLogicalAddress()}.
+         */
+        @NonNull
+        public Builder setLogicalAddress(int logicalAddress) {
+            mLogicalAddress = logicalAddress;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeviceType()}.
+         */
+        @NonNull
+        public Builder setDeviceType(int deviceType) {
+            mDeviceType = deviceType;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getCecVersion()}.
+         */
+        @NonNull
+        public Builder setCecVersion(int hdmiCecVersion) {
+            mCecVersion = hdmiCecVersion;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getVendorId()}.
+         */
+        @NonNull
+        public Builder setVendorId(int vendorId) {
+            mVendorId = vendorId;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDisplayName()}.
+         */
+        @NonNull
+        public Builder setDisplayName(@NonNull String displayName) {
+            mDisplayName = displayName;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDevicePowerStatus()}.
+         */
+        @NonNull
+        public Builder setDevicePowerStatus(int devicePowerStatus) {
+            mDevicePowerStatus = devicePowerStatus;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeviceFeatures()}.
+         */
+        @NonNull
+        public Builder setDeviceFeatures(DeviceFeatures deviceFeatures) {
+            this.mDeviceFeatures = deviceFeatures;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getDeviceId()}.
+         */
+        @NonNull
+        public Builder setDeviceId(int deviceId) {
+            mDeviceId = deviceId;
+            return this;
+        }
+
+        /**
+         * Sets the value for {@link #getAdopterId()}.
+         */
+        @NonNull
+        public Builder setAdopterId(int adopterId) {
+            mAdopterId = adopterId;
+            return this;
+        }
+
+        /**
+         * Updates the value for {@link #getDeviceFeatures()} with a new set of device features.
+         * New information overrides the old, except when feature support was unknown.
+         */
+        @NonNull
+        public Builder updateDeviceFeatures(DeviceFeatures deviceFeatures) {
+            mDeviceFeatures = mDeviceFeatures.toBuilder().update(deviceFeatures).build();
+            return this;
+        }
+    }
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f50aa99..147138e 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -69,6 +69,8 @@
 
     int getMultipathPreference(in Network network);
 
+    SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
+    void onStatsProviderWarningOrLimitReached();
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7ebb646..426fc61 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -535,6 +535,46 @@
     }
 
     /**
+     * Get subscription plan for the given networkTemplate.
+     *
+     * @param template the networkTemplate to get the subscription plan for.
+     * @return the active {@link SubscriptionPlan} for the given template, or
+     *         {@code null} if not found.
+     * @hide
+     */
+    @Nullable
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+        try {
+            return mService.getSubscriptionPlan(template);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+     * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void onStatsProviderWarningOrLimitReached() {
+        try {
+            mService.onStatsProviderWarningOrLimitReached();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Resets network policy settings back to factory defaults.
      *
      * @hide
diff --git a/core/java/android/net/PrivateDnsConnectivityChecker.java b/core/java/android/net/PrivateDnsConnectivityChecker.java
index cfd458c..ac97b36 100644
--- a/core/java/android/net/PrivateDnsConnectivityChecker.java
+++ b/core/java/android/net/PrivateDnsConnectivityChecker.java
@@ -44,7 +44,7 @@
      */
     public static boolean canConnectToPrivateDnsServer(@NonNull String hostname) {
         final SocketFactory factory = SSLSocketFactory.getDefault();
-        TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_APP);
+        TrafficStats.setThreadStatsTagApp();
 
         try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
             socket.setSoTimeout(CONNECTION_TIMEOUT_MS);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 5c28553..3193826 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -96,6 +96,141 @@
      */
     public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
 
+    /**
+     * Action sent in the intent when an error occurred.
+     *
+     * @hide
+     */
+    public static final String ACTION_VPN_MANAGER_ERROR = "android.net.action.VPN_MANAGER_ERROR";
+
+    /**
+     * An IKE protocol error. Codes are the codes from IkeProtocolException, RFC 7296.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_IKE = "android.net.category.ERROR_IKE";
+
+    /**
+     * User deactivated the VPN, either by turning it off or selecting a different VPN provider.
+     * The error code is always 0.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_USER_DEACTIVATED =
+            "android.net.category.ERROR_USER_DEACTIVATED";
+
+    /**
+     * Network error. Error codes are ERROR_CODE_NETWORK_*.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_NETWORK = "android.net.category.ERROR_NETWORK";
+
+    /**
+     * The key of the session that experienced this error, as returned by
+     * startProvisionedVpnProfileSession.
+     *
+     * @hide
+     */
+    public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY";
+
+    /**
+     * Extra for the Network object that was the underlying network at the time of the failure, or
+     * null if none.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
+
+    /**
+     * The NetworkCapabilities of the underlying network.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES =
+            "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
+
+    /**
+     * The LinkProperties of the underlying network.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_LINK_PROPERTIES =
+            "android.net.extra.UNDERLYING_LINK_PROPERTIES";
+
+    /**
+     * A long timestamp with SystemClock.elapsedRealtime base for when the event happened.
+     *
+     * @hide
+     */
+    public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+
+    /**
+     * Extra for the error type. This is ERROR_NOT_RECOVERABLE or ERROR_RECOVERABLE.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR_TYPE = "android.net.extra.ERROR_TYPE";
+
+    /**
+     * Extra for the error code. The value will be 0 for CATEGORY_ERROR_USER_DEACTIVATED, one of
+     * ERROR_CODE_NETWORK_* for ERROR_CATEGORY_NETWORK or one of values defined in
+     * IkeProtocolException#ErrorType for CATEGORY_ERROR_IKE.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE";
+
+    /**
+     * This error is fatal, e.g. the VPN was disabled or configuration error. The stack will not
+     * retry connection.
+     *
+     * @hide
+     */
+    public static final int ERROR_NOT_RECOVERABLE = 1;
+
+    /**
+     * The stack experienced an error but will retry with exponential backoff, e.g. network timeout.
+     *
+     * @hide
+     */
+    public static final int ERROR_RECOVERABLE = 2;
+
+    /**
+     * An error code to indicate that there was an UnknownHostException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_UNKNOWN_HOST = 0;
+
+    /**
+     * An error code to indicate that there is a SocketTimeoutException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_TIMEOUT = 1;
+
+    /**
+     * An error code to indicate that the connection is refused.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_CONNECT = 2;
+
+    /**
+     * An error code to indicate the connection was reset. (e.g. SocketException)
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_CONNECTION_RESET = 3;
+
+    /**
+     * An error code to indicate that there is an IOException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_IO = 4;
+
     /** @hide */
     @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
             TYPE_VPN_OEM})
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
index febd9b4..3f7521a 100644
--- a/core/java/android/net/annotations/PolicyDirection.java
+++ b/core/java/android/net/annotations/PolicyDirection.java
@@ -24,10 +24,6 @@
 
 /**
  * IPsec traffic direction.
- *
- * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
- * to allow others to statically include it.
- *
  * @hide
  */
 @IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 125b573..69e6313 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -63,13 +63,22 @@
     private final int mOpportunisticMatchCriteria;
 
     private VcnCellUnderlyingNetworkTemplate(
-            int networkQuality,
             int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps,
             Set<String> allowedNetworkPlmnIds,
             Set<Integer> allowedSpecificCarrierIds,
             int roamingMatchCriteria,
             int opportunisticMatchCriteria) {
-        super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, meteredMatchCriteria);
+        super(
+                NETWORK_PRIORITY_TYPE_CELL,
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps);
         mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
         mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
         mRoamingMatchCriteria = roamingMatchCriteria;
@@ -109,9 +118,17 @@
             @NonNull PersistableBundle in) {
         Objects.requireNonNull(in, "PersistableBundle is null");
 
-        final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
         final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
 
+        final int minEntryUpstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitUpstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minEntryDownstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitDownstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
         final PersistableBundle plmnIdsBundle =
                 in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
         Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null");
@@ -131,8 +148,11 @@
         final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
 
         return new VcnCellUnderlyingNetworkTemplate(
-                networkQuality,
                 meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps,
                 allowedNetworkPlmnIds,
                 allowedSpecificCarrierIds,
                 roamingMatchCriteria,
@@ -243,7 +263,6 @@
 
     /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
     public static final class Builder {
-        private int mNetworkQuality = NETWORK_QUALITY_ANY;
         private int mMeteredMatchCriteria = MATCH_ANY;
 
         @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@@ -252,29 +271,15 @@
         private int mRoamingMatchCriteria = MATCH_ANY;
         private int mOpportunisticMatchCriteria = MATCH_ANY;
 
+        private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
         /** Construct a Builder object. */
         public Builder() {}
 
         /**
-         * Set the required network quality to match this template.
-         *
-         * <p>Network quality is a aggregation of multiple signals that reflect the network link
-         * metrics. For example, the network validation bit (see {@link
-         * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
-         * and signal strength.
-         *
-         * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
-         * @hide
-         */
-        @NonNull
-        public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
-            validateNetworkQuality(networkQuality);
-
-            mNetworkQuality = networkQuality;
-            return this;
-        }
-
-        /**
          * Set the matching criteria for metered networks.
          *
          * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
@@ -369,12 +374,92 @@
             return this;
         }
 
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+         *     that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+         *     that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinUpstreamBandwidthKbps(
+                int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+            validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+            mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+            mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+         *     networks that ARE NOT the already-selected underlying network, or {@code 0} to
+         *     disable this requirement. Disabled by default.
+         * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+         *     network that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinDownstreamBandwidthKbps(
+                int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+            validateMinBandwidthKbps(
+                    minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+            mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+            mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+            return this;
+        }
+
         /** Build the VcnCellUnderlyingNetworkTemplate. */
         @NonNull
         public VcnCellUnderlyingNetworkTemplate build() {
             return new VcnCellUnderlyingNetworkTemplate(
-                    mNetworkQuality,
                     mMeteredMatchCriteria,
+                    mMinEntryUpstreamBandwidthKbps,
+                    mMinExitUpstreamBandwidthKbps,
+                    mMinEntryDownstreamBandwidthKbps,
+                    mMinExitDownstreamBandwidthKbps,
                     mAllowedNetworkPlmnIds,
                     mAllowedSpecificCarrierIds,
                     mRoamingMatchCriteria,
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 92956e8..a6830b7 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,7 +17,6 @@
 
 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
@@ -169,18 +168,15 @@
     static {
         DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnCellUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .setOpportunistic(MATCH_REQUIRED)
                         .build());
 
         DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .build());
 
         DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnCellUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .build());
     }
 
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 60fc936..3a9ca3e 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -48,23 +48,6 @@
     /** @hide */
     static final int NETWORK_PRIORITY_TYPE_CELL = 2;
 
-    /** Denotes that any network quality is acceptable. @hide */
-    public static final int NETWORK_QUALITY_ANY = 0;
-    /** Denotes that network quality needs to be OK. @hide */
-    public static final int NETWORK_QUALITY_OK = 100000;
-
-    private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>();
-
-    static {
-        NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_ANY, "NETWORK_QUALITY_ANY");
-        NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_OK, "NETWORK_QUALITY_OK");
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
-    public @interface NetworkQuality {}
-
     /**
      * Used to configure the matching criteria of a network characteristic. This may include network
      * capabilities, or cellular subscription information. Denotes that networks with or without the
@@ -103,44 +86,73 @@
     private final int mNetworkPriorityType;
 
     /** @hide */
-    static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
-
-    private final int mNetworkQuality;
-
-    /** @hide */
     static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
 
     private final int mMeteredMatchCriteria;
 
     /** @hide */
+    public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0;
+
+    /** @hide */
+    static final String MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinEntryUpstreamBandwidthKbps";
+
+    private final int mMinEntryUpstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitUpstreamBandwidthKbps";
+
+    private final int mMinExitUpstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY =
+            "mMinEntryDownstreamBandwidthKbps";
+
+    private final int mMinEntryDownstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitDownstreamBandwidthKbps";
+
+    private final int mMinExitDownstreamBandwidthKbps;
+
+    /** @hide */
     VcnUnderlyingNetworkTemplate(
-            int networkPriorityType, int networkQuality, int meteredMatchCriteria) {
+            int networkPriorityType,
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps) {
         mNetworkPriorityType = networkPriorityType;
-        mNetworkQuality = networkQuality;
         mMeteredMatchCriteria = meteredMatchCriteria;
+        mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+        mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+        mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+        mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
     }
 
     /** @hide */
-    static void validateNetworkQuality(int networkQuality) {
+    static void validateMatchCriteria(int matchCriteria, String matchingCapability) {
         Preconditions.checkArgument(
-                networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
-                "Invalid networkQuality:" + networkQuality);
+                MATCH_CRITERIA_TO_STRING_MAP.contains(matchCriteria),
+                "Invalid matching criteria: " + matchCriteria + " for " + matchingCapability);
     }
 
     /** @hide */
-    static void validateMatchCriteria(int meteredMatchCriteria, String matchingCapability) {
+    static void validateMinBandwidthKbps(int minEntryBandwidth, int minExitBandwidth) {
         Preconditions.checkArgument(
-                MATCH_CRITERIA_TO_STRING_MAP.contains(meteredMatchCriteria),
-                "Invalid matching criteria: "
-                        + meteredMatchCriteria
-                        + " for "
-                        + matchingCapability);
+                minEntryBandwidth >= 0, "Invalid minEntryBandwidth, must be >= 0");
+        Preconditions.checkArgument(
+                minExitBandwidth >= 0, "Invalid minExitBandwidth, must be >= 0");
+        Preconditions.checkArgument(
+                minEntryBandwidth >= minExitBandwidth,
+                "Minimum entry bandwidth must be >= exit bandwidth");
     }
 
     /** @hide */
     protected void validate() {
-        validateNetworkQuality(mNetworkQuality);
         validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
+        validateMinBandwidthKbps(mMinEntryUpstreamBandwidthKbps, mMinExitUpstreamBandwidthKbps);
+        validateMinBandwidthKbps(mMinEntryDownstreamBandwidthKbps, mMinExitDownstreamBandwidthKbps);
     }
 
     /** @hide */
@@ -168,15 +180,24 @@
         final PersistableBundle result = new PersistableBundle();
 
         result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
-        result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
         result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
+        result.putInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryUpstreamBandwidthKbps);
+        result.putInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinExitUpstreamBandwidthKbps);
+        result.putInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryDownstreamBandwidthKbps);
+        result.putInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinExitDownstreamBandwidthKbps);
 
         return result;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mNetworkPriorityType, mNetworkQuality, mMeteredMatchCriteria);
+        return Objects.hash(
+                mNetworkPriorityType,
+                mMeteredMatchCriteria,
+                mMinEntryUpstreamBandwidthKbps,
+                mMinExitUpstreamBandwidthKbps,
+                mMinEntryDownstreamBandwidthKbps,
+                mMinExitDownstreamBandwidthKbps);
     }
 
     @Override
@@ -187,8 +208,11 @@
 
         final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
         return mNetworkPriorityType == rhs.mNetworkPriorityType
-                && mNetworkQuality == rhs.mNetworkQuality
-                && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria;
+                && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria
+                && mMinEntryUpstreamBandwidthKbps == rhs.mMinEntryUpstreamBandwidthKbps
+                && mMinExitUpstreamBandwidthKbps == rhs.mMinExitUpstreamBandwidthKbps
+                && mMinEntryDownstreamBandwidthKbps == rhs.mMinEntryDownstreamBandwidthKbps
+                && mMinExitDownstreamBandwidthKbps == rhs.mMinExitDownstreamBandwidthKbps;
     }
 
     /** @hide */
@@ -197,8 +221,8 @@
     }
 
     /** @hide */
-    static String getMatchCriteriaString(int meteredMatchCriteria) {
-        return getNameString(MATCH_CRITERIA_TO_STRING_MAP, meteredMatchCriteria);
+    static String getMatchCriteriaString(int matchCriteria) {
+        return getNameString(MATCH_CRITERIA_TO_STRING_MAP, matchCriteria);
     }
 
     /** @hide */
@@ -213,34 +237,63 @@
         pw.println(this.getClass().getSimpleName() + ":");
         pw.increaseIndent();
 
-        pw.println(
-                "mNetworkQuality: "
-                        + getNameString(NETWORK_QUALITY_TO_STRING_MAP, mNetworkQuality));
         pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+        pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+        pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+        pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+        pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
         dumpTransportSpecificFields(pw);
 
         pw.decreaseIndent();
     }
 
     /**
-     * Retrieve the required network quality to match this template.
-     *
-     * @see Builder#setNetworkQuality(int)
-     * @hide
-     */
-    @NetworkQuality
-    public int getNetworkQuality() {
-        return mNetworkQuality;
-    }
-
-    /**
      * Return the matching criteria for metered networks.
      *
      * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
      * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
      */
-    @MatchCriteria
     public int getMetered() {
         return mMeteredMatchCriteria;
     }
+
+    /**
+     * Returns the minimum entry upstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     */
+    public int getMinEntryUpstreamBandwidthKbps() {
+        return mMinEntryUpstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum exit upstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     */
+    public int getMinExitUpstreamBandwidthKbps() {
+        return mMinExitUpstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum entry downstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     */
+    public int getMinEntryDownstreamBandwidthKbps() {
+        return mMinEntryDownstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum exit downstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     */
+    public int getMinExitDownstreamBandwidthKbps() {
+        return mMinExitDownstreamBandwidthKbps;
+    }
 }
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 272ca9d..23a07ab 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -46,8 +46,19 @@
     @Nullable private final Set<String> mSsids;
 
     private VcnWifiUnderlyingNetworkTemplate(
-            int networkQuality, int meteredMatchCriteria, Set<String> ssids) {
-        super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, meteredMatchCriteria);
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps,
+            Set<String> ssids) {
+        super(
+                NETWORK_PRIORITY_TYPE_WIFI,
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps);
         mSsids = new ArraySet<>(ssids);
 
         validate();
@@ -75,15 +86,29 @@
             @NonNull PersistableBundle in) {
         Objects.requireNonNull(in, "PersistableBundle is null");
 
-        final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
         final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
 
+        final int minEntryUpstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitUpstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minEntryDownstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitDownstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
         final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY);
         Objects.requireNonNull(ssidsBundle, "ssidsBundle is null");
         final Set<String> ssids =
                 new ArraySet<String>(
                         PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER));
-        return new VcnWifiUnderlyingNetworkTemplate(networkQuality, meteredMatchCriteria, ssids);
+        return new VcnWifiUnderlyingNetworkTemplate(
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps,
+                ssids);
     }
 
     /** @hide */
@@ -137,33 +162,18 @@
 
     /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
     public static final class Builder {
-        private int mNetworkQuality = NETWORK_QUALITY_ANY;
         private int mMeteredMatchCriteria = MATCH_ANY;
         @NonNull private final Set<String> mSsids = new ArraySet<>();
 
+        private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
         /** Construct a Builder object. */
         public Builder() {}
 
         /**
-         * Set the required network quality to match this template.
-         *
-         * <p>Network quality is a aggregation of multiple signals that reflect the network link
-         * metrics. For example, the network validation bit (see {@link
-         * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
-         * and signal strength.
-         *
-         * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
-         * @hide
-         */
-        @NonNull
-        public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
-            validateNetworkQuality(networkQuality);
-
-            mNetworkQuality = networkQuality;
-            return this;
-        }
-
-        /**
          * Set the matching criteria for metered networks.
          *
          * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
@@ -200,11 +210,93 @@
             return this;
         }
 
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+         *     that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+         *     that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinUpstreamBandwidthKbps(
+                int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+            validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+            mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+            mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+         *     networks that ARE NOT the already-selected underlying network, or {@code 0} to
+         *     disable this requirement. Disabled by default.
+         * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+         *     network that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinDownstreamBandwidthKbps(
+                int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+            validateMinBandwidthKbps(
+                    minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+            mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+            mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+            return this;
+        }
+
         /** Build the VcnWifiUnderlyingNetworkTemplate. */
         @NonNull
         public VcnWifiUnderlyingNetworkTemplate build() {
             return new VcnWifiUnderlyingNetworkTemplate(
-                    mNetworkQuality, mMeteredMatchCriteria, mSsids);
+                    mMeteredMatchCriteria,
+                    mMinEntryUpstreamBandwidthKbps,
+                    mMinExitUpstreamBandwidthKbps,
+                    mMinEntryDownstreamBandwidthKbps,
+                    mMinExitDownstreamBandwidthKbps,
+                    mSsids);
         }
     }
 }
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index c607195..d2f788f 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -191,6 +191,7 @@
     private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
             POWER_COMPONENT_CPU,
             POWER_COMPONENT_MOBILE_RADIO,
+            POWER_COMPONENT_WIFI,
     };
 
     static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 520730f..a453887 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -461,6 +461,12 @@
         public abstract long getCountLocked(int which);
 
         /**
+         * Returns the count accumulated by this Counter for the specified process state.
+         * If the counter does not support per-procstate tracking, returns 0.
+         */
+        public abstract long getCountForProcessState(@BatteryConsumer.ProcessState int procState);
+
+        /**
          * Temporary for debugging.
          */
         public abstract void logState(Printer pw, String prefix);
@@ -1096,6 +1102,17 @@
         public abstract long getWifiMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the uid's wifi usage when in the
+         * specified process state.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getWifiMeasuredBatteryConsumptionUC(
+                @BatteryConsumer.ProcessState int processState);
+
+
+        /**
          * Returns the battery consumption (in microcoulombs) used by this uid for each
          * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
          * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 429450c..80cf2f8 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -311,6 +311,7 @@
     private final long mObject;
 
     private IInterface mOwner;
+    @Nullable
     private String mDescriptor;
     private volatile String[] mTransactionTraceNames = null;
     private volatile String mSimpleDescriptor = null;
@@ -930,8 +931,8 @@
                 transactionNames[i] = buf.toString();
                 buf.setLength(0);
             }
-            mTransactionTraceNames = transactionNames;
             mSimpleDescriptor = descriptor;
+            mTransactionTraceNames = transactionNames;
         }
         final int index = transactionCode - FIRST_CALL_TRANSACTION;
         if (index < 0 || index >= mTransactionTraceNames.length) {
@@ -940,13 +941,19 @@
         return mTransactionTraceNames[index];
     }
 
-    private String getSimpleDescriptor() {
-        final int dot = mDescriptor.lastIndexOf(".");
+    private @NonNull String getSimpleDescriptor() {
+        String descriptor = mDescriptor;
+        if (descriptor == null) {
+            // Just "Binder" to avoid null checks in transaction name tracing.
+            return "Binder";
+        }
+
+        final int dot = descriptor.lastIndexOf(".");
         if (dot > 0) {
             // Strip the package name
-            return mDescriptor.substring(dot + 1);
+            return descriptor.substring(dot + 1);
         }
-        return mDescriptor;
+        return descriptor;
     }
 
     /**
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3f42164..fe86874 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -90,7 +90,7 @@
     Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
-    int removeUserOrSetEphemeral(int userId, boolean evenWhenDisallowed);
+    int removeUserWhenPossible(int userId, boolean overrideDevicePolicy);
     boolean markGuestForDeletion(int userId);
     UserInfo findCurrentGuestUser();
     boolean isQuietModeEnabled(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index edf6280..190f5f1 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4750,7 +4750,7 @@
     public int removeUserWhenPossible(@NonNull UserHandle user,
             boolean overrideDevicePolicy) {
         try {
-            return mService.removeUserOrSetEphemeral(user.getIdentifier(), overrideDevicePolicy);
+            return mService.removeUserWhenPossible(user.getIdentifier(), overrideDevicePolicy);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -4777,7 +4777,7 @@
     public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
             boolean evenWhenDisallowed) {
         try {
-            return mService.removeUserOrSetEphemeral(userId, evenWhenDisallowed);
+            return mService.removeUserWhenPossible(userId, evenWhenDisallowed);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 72e2863..5cdb1fd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10525,7 +10525,7 @@
                 DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
         })
         @Retention(RetentionPolicy.SOURCE)
-        @interface DeviceStateRotationLockSetting {
+        public @interface DeviceStateRotationLockSetting {
         }
 
         /**
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 61277e2..22ed1b8 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -114,15 +114,47 @@
      */
     public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 1 << 1;
 
+    /**
+     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating the platform should
+     * automatically remove trust after some conditions are met (detailed below) with the option for
+     * the agent to renew the trust again later.
+     *
+     * <p>After this is called, the agent will grant trust until the platform thinks an active user
+     * is no longer using that trust. For example, if the user dismisses keyguard, the platform will
+     * remove trust (this does not automatically lock the device).
+     *
+     * <p>When the platform internally removes the agent's trust in this manner, an agent can
+     * re-grant it (via a call to grantTrust) without the user having to unlock the device through
+     * another method (e.g. PIN). This renewable state only persists for a limited time.
+     *
+     * TODO(b/213631675): Remove @hide
+     * @hide
+     */
+    public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 1 << 2;
+
+    /**
+     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that the message should
+     * be displayed to the user.
+     *
+     * Without this flag, the message passed to {@code grantTrust} is only used for debugging
+     * purposes. With the flag, it may be displayed to the user as the reason why the device is
+     * unlocked.
+     *
+     * TODO(b/213911325): Remove @hide
+     * @hide
+     */
+    public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "FLAG_GRANT_TRUST_" }, value = {
             FLAG_GRANT_TRUST_INITIATED_BY_USER,
             FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
+            FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE,
+            FLAG_GRANT_TRUST_DISPLAY_MESSAGE,
     })
     public @interface GrantTrustFlags {}
 
-
     /**
      * Int enum indicating that escrow token is active.
      * See {@link #onEscrowTokenStateReceived(long, int)}
@@ -265,6 +297,22 @@
     }
 
     /**
+     * Called when the user has interacted with the locked device such that they likely want it
+     * to be unlocked. This approximates the timing when, for example, the platform would check for
+     * face authentication to unlock the device.
+     *
+     * To attempt to unlock the device, the agent needs to call
+     * {@link #grantTrust(CharSequence, long, int)}.
+     *
+     * @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
+     *
+     * TODO(b/213631672): Hook up call from system server & SystemUI, then un-hide
+     * @hide
+     */
+    public void onUserRequestedUnlock() {
+    }
+
+    /**
      * Called when the timeout provided by the agent expires.  Note that this may be called earlier
      * than requested by the agent if the trust timeout is adjusted by the system or
      * {@link DevicePolicyManager}.  The agent is expected to re-evaluate the trust state and only
@@ -564,6 +612,22 @@
     }
 
     /**
+     * Locks the user.
+     *
+     * This revokes any trust granted by this agent and shows keyguard for the user if it is not
+     * currently shown for them. Other users are not affected. Note that this is in contrast to
+     * {@link #revokeTrust()} which does not show keyguard if it is not already shown.
+     *
+     * If the user has no auth method specified, then keyguard will still be shown but can be
+     * dismissed normally.
+     *
+     * TODO(b/213631675): Implement & make public
+     * @hide
+     */
+    public final void lockUser() {
+    }
+
+    /**
      * Request showing a transient error message on the keyguard.
      * The message will be visible on the lock screen or always on display if possible but can be
      * overridden by other keyguard events of higher priority - eg. fingerprint auth error.
diff --git a/core/java/android/util/Dumpable.java b/core/java/android/util/Dumpable.java
new file mode 100644
index 0000000..79c576d
--- /dev/null
+++ b/core/java/android/util/Dumpable.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents an object whose state can be dumped into a {@link PrintWriter}.
+ */
+public interface Dumpable {
+
+    /**
+     * Gets the name of the {@link Dumpable}.
+     *
+     * @return class name, by default.
+     */
+    @NonNull
+    default String getDumpableName() {
+        return getClass().getName();
+    }
+
+    //TODO(b/149254050): decide whether it should take a ParcelFileDescription as well.
+
+    /**
+     * Dumps the internal state into the given {@code writer}.
+     *
+     * @param writer writer to be written to
+     * @param args optional list of arguments
+     */
+    void dump(@NonNull PrintWriter writer, @Nullable String[] args);
+}
diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java
new file mode 100644
index 0000000..04d19dc
--- /dev/null
+++ b/core/java/android/util/DumpableContainer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.NonNull;
+
+/**
+ * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself
+ * is dumped.
+ */
+public interface DumpableContainer {
+
+    /**
+     * Adds the given {@link Dumpable dumpable} to the container.
+     *
+     * <p>If a dumpable with the same {@link Dumpable#getDumpableName() name} was added before, this
+     * call is ignored.
+     *
+     * @param dumpable dumpable to be added.
+     *
+     * @throws IllegalArgumentException if the {@link Dumpable#getDumpableName() dumpable name} is
+     * {@code null}.
+     *
+     * @return {@code true} if the dumpable was added, {@code false} if the call was ignored.
+     */
+    boolean addDumpable(@NonNull Dumpable dumpable);
+}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index be172f7..9b8523f 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -19,6 +19,9 @@
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.FrameInfo;
@@ -151,10 +154,15 @@
     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
 
-    // All frame callbacks posted by applications have this token.
+    // All frame callbacks posted by applications have this token or EXTENDED_FRAME_CALLBACK_TOKEN.
     private static final Object FRAME_CALLBACK_TOKEN = new Object() {
         public String toString() { return "FRAME_CALLBACK_TOKEN"; }
     };
+    private static final Object EXTENDED_FRAME_CALLBACK_TOKEN = new Object() {
+        public String toString() {
+            return "EXTENDED_FRAME_CALLBACK_TOKEN";
+        }
+    };
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final Object mLock = new Object();
@@ -484,6 +492,24 @@
     }
 
     /**
+     * Posts an extended frame callback to run on the next frame.
+     * <p>
+     * The callback runs once then is automatically removed.
+     * </p>
+     *
+     * @param callback The extended frame callback to run during the next frame.
+     *
+     * @see #removeExtendedFrameCallback
+     */
+    public void postExtendedFrameCallback(@NonNull ExtendedFrameCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN, 0);
+    }
+
+    /**
      * Removes callbacks that have the specified action and token.
      *
      * @param callbackType The callback type.
@@ -573,6 +599,21 @@
     }
 
     /**
+     * Removes a previously posted extended frame callback.
+     *
+     * @param callback The extended frame callback to remove.
+     *
+     * @see #postExtendedFrameCallback
+     */
+    public void removeExtendedFrameCallback(@Nullable ExtendedFrameCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        removeCallbacksInternal(CALLBACK_ANIMATION, callback, EXTENDED_FRAME_CALLBACK_TOKEN);
+    }
+
+    /**
      * Gets the time when the current frame started.
      * <p>
      * This method provides the time in milliseconds when the frame started being rendered.
@@ -673,7 +714,7 @@
      * @hide
      */
     public long getVsyncId() {
-        return mLastVsyncEventData.id;
+        return mLastVsyncEventData.preferredFrameTimeline().vsyncId;
     }
 
     /**
@@ -684,7 +725,7 @@
      * @hide
      */
     public long getFrameDeadline() {
-        return mLastVsyncEventData.frameDeadline;
+        return mLastVsyncEventData.preferredFrameTimeline().deadline;
     }
 
     void setFPSDivisor(int divisor) {
@@ -705,8 +746,9 @@
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                 Trace.traceBegin(Trace.TRACE_TAG_VIEW,
-                        "Choreographer#doFrame " + vsyncEventData.id);
+                        "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId);
             }
+            FrameData frameData = new FrameData(frameTimeNanos, vsyncEventData);
             synchronized (mLock) {
                 if (!mFrameScheduled) {
                     traceMessage("Frame not scheduled");
@@ -737,6 +779,7 @@
                                 + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                     }
                     frameTimeNanos = startNanos - lastFrameOffset;
+                    frameData.setFrameTimeNanos(-lastFrameOffset);
                 }
 
                 if (frameTimeNanos < mLastFrameTimeNanos) {
@@ -758,8 +801,10 @@
                     }
                 }
 
-                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
-                        vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
+                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos,
+                        vsyncEventData.preferredFrameTimeline().vsyncId,
+                        vsyncEventData.preferredFrameTimeline().deadline, startNanos,
+                        vsyncEventData.frameInterval);
                 mFrameScheduled = false;
                 mLastFrameTimeNanos = frameTimeNanos;
                 mLastFrameIntervalNanos = frameIntervalNanos;
@@ -769,17 +814,17 @@
             AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
 
             mFrameInfo.markInputHandlingStart();
-            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos);
 
             mFrameInfo.markAnimationsStart();
-            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
-            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
+            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData,
                     frameIntervalNanos);
 
             mFrameInfo.markPerformTraversalsStart();
-            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos);
 
-            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
+            doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, frameIntervalNanos);
         } finally {
             AnimationUtils.unlockAnimationClock();
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -793,8 +838,9 @@
         }
     }
 
-    void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
+    void doCallbacks(int callbackType, FrameData frameData, long frameIntervalNanos) {
         CallbackRecord callbacks;
+        long frameTimeNanos = frameData.mFrameTimeNanos;
         synchronized (mLock) {
             // We use "now" to determine when callbacks become due because it's possible
             // for earlier processing phases in a frame to post callbacks that should run
@@ -831,6 +877,7 @@
                     }
                     frameTimeNanos = now - lastFrameOffset;
                     mLastFrameTimeNanos = frameTimeNanos;
+                    frameData.setFrameTimeNanos(frameTimeNanos);
                 }
             }
         }
@@ -842,7 +889,7 @@
                             + ", action=" + c.action + ", token=" + c.token
                             + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                 }
-                c.run(frameTimeNanos);
+                c.run(frameData);
             }
         } finally {
             synchronized (mLock) {
@@ -942,6 +989,130 @@
         public void doFrame(long frameTimeNanos);
     }
 
+    /** Holds data that describes one possible VSync frame event to render at. */
+    public static class FrameTimeline {
+        static final FrameTimeline INVALID_FRAME_TIMELINE = new FrameTimeline(
+                FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE);
+
+        FrameTimeline(long vsyncId, long expectedPresentTimeNanos, long deadlineNanos) {
+            this.mVsyncId = vsyncId;
+            this.mExpectedPresentTimeNanos = expectedPresentTimeNanos;
+            this.mDeadlineNanos = deadlineNanos;
+        }
+
+        private long mVsyncId;
+        private long mExpectedPresentTimeNanos;
+        private long mDeadlineNanos;
+
+        /**
+         * The id that corresponds to this frame timeline, used to correlate a frame
+         * produced by HWUI with the timeline data stored in Surface Flinger.
+         */
+        public long getVsyncId() {
+            return mVsyncId;
+        }
+
+        /** Sets the vsync ID. */
+        void resetVsyncId() {
+            mVsyncId = FrameInfo.INVALID_VSYNC_ID;
+        }
+
+        /**
+         * The time in {@link System#nanoTime()} timebase which this frame is expected to be
+         * presented.
+         */
+        public long getExpectedPresentTimeNanos() {
+            return mExpectedPresentTimeNanos;
+        }
+
+        /**
+         * The time in  {@link System#nanoTime()} timebase which this frame needs to be ready by.
+         */
+        public long getDeadlineNanos() {
+            return mDeadlineNanos;
+        }
+    }
+
+    /**
+     * The payload for {@link ExtendedFrameCallback} which includes frame information such as when
+     * the frame started being rendered, and multiple possible frame timelines and their
+     * information including deadline and expected present time.
+     */
+    public static class FrameData {
+        static final FrameTimeline[] INVALID_FRAME_TIMELINES = new FrameTimeline[0];
+        FrameData() {
+            this.mFrameTimelines = INVALID_FRAME_TIMELINES;
+            this.mPreferredFrameTimeline = FrameTimeline.INVALID_FRAME_TIMELINE;
+        }
+
+        FrameData(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
+            FrameTimeline[] frameTimelines =
+                    new FrameTimeline[vsyncEventData.frameTimelines.length];
+            for (int i = 0; i <  vsyncEventData.frameTimelines.length; i++) {
+                DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline =
+                        vsyncEventData.frameTimelines[i];
+                frameTimelines[i] = new FrameTimeline(frameTimeline.vsyncId,
+                        frameTimeline.expectedPresentTime, frameTimeline.deadline);
+            }
+            this.mFrameTimeNanos = frameTimeNanos;
+            this.mFrameTimelines = frameTimelines;
+            this.mPreferredFrameTimeline =
+                    frameTimelines[vsyncEventData.preferredFrameTimelineIndex];
+        }
+
+        private long mFrameTimeNanos;
+        private final FrameTimeline[] mFrameTimelines;
+        private final FrameTimeline mPreferredFrameTimeline;
+
+        void setFrameTimeNanos(long frameTimeNanos) {
+            mFrameTimeNanos = frameTimeNanos;
+            for (FrameTimeline ft : mFrameTimelines) {
+                // The ID is no longer valid because the frame time that was registered with the ID
+                // no longer matches.
+                // TODO(b/205721584): Ask SF for valid vsync information.
+                ft.resetVsyncId();
+            }
+        }
+
+        /** The time in nanoseconds when the frame started being rendered. */
+        public long getFrameTimeNanos() {
+            return mFrameTimeNanos;
+        }
+
+        /** The possible frame timelines, sorted chronologically. */
+        @NonNull
+        @SuppressLint("ArrayReturn") // For API consistency and speed.
+        public FrameTimeline[] getFrameTimelines() {
+            return mFrameTimelines;
+        }
+
+        /** The platform-preferred frame timeline. */
+        @NonNull
+        public FrameTimeline getPreferredFrameTimeline() {
+            return mPreferredFrameTimeline;
+        }
+    }
+
+    /**
+     * Implement this interface to receive a callback to start the next frame. The callback is
+     * invoked on the {@link Looper} thread to which the {@link Choreographer} is attached. The
+     * callback payload contains information about multiple possible frames, allowing choice of
+     * the appropriate frame based on latency requirements.
+     *
+     * @see FrameCallback
+     */
+    public interface ExtendedFrameCallback {
+        /**
+         * Called when a new display frame is being rendered.
+         *
+         * @param data The payload which includes frame information. Divide nanosecond values by
+         *             {@code 1000000} to convert it to the {@link SystemClock#uptimeMillis()}
+         *             time base.
+         * @see FrameCallback#doFrame
+         **/
+        void onVsync(@NonNull FrameData data);
+    }
+
     private final class FrameHandler extends Handler {
         public FrameHandler(Looper looper) {
             super(looper);
@@ -983,7 +1154,8 @@
             try {
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                     Trace.traceBegin(Trace.TRACE_TAG_VIEW,
-                            "Choreographer#onVsync " + vsyncEventData.id);
+                            "Choreographer#onVsync "
+                                    + vsyncEventData.preferredFrameTimeline().vsyncId);
                 }
                 // Post the vsync event to the Handler.
                 // The idea is to prevent incoming vsync events from completely starving
@@ -1026,7 +1198,9 @@
     private static final class CallbackRecord {
         public CallbackRecord next;
         public long dueTime;
-        public Object action; // Runnable or FrameCallback
+        /** Runnable or FrameCallback or ExtendedFrameCallback object. */
+        public Object action;
+        /** Denotes the action type. */
         public Object token;
 
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1037,6 +1211,14 @@
                 ((Runnable)action).run();
             }
         }
+
+        void run(FrameData frameData) {
+            if (token == EXTENDED_FRAME_CALLBACK_TOKEN) {
+                ((ExtendedFrameCallback) action).onVsync(frameData);
+            } else {
+                run(frameData.getFrameTimeNanos());
+            }
+        }
     }
 
     private final class CallbackQueue {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3cc51c7..70266c1 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
 import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -138,6 +139,24 @@
     public static final int INVALID_DISPLAY = -1;
 
     /**
+     * Invalid resolution width.
+     * @hide
+     */
+    public static final int INVALID_DISPLAY_WIDTH = -1;
+
+    /**
+     * Invalid resolution height.
+     * @hide
+     */
+    public static final int INVALID_DISPLAY_HEIGHT = -1;
+
+    /**
+     * Invalid refresh rate.
+     * @hide
+     */
+    public static final float INVALID_DISPLAY_REFRESH_RATE = 0.0f;
+
+    /**
      * The default display group id, which is the display group id of the primary display assuming
      * there is one.
      * @hide
@@ -1170,6 +1189,49 @@
     }
 
     /**
+     * Sets the default {@link Display.Mode} to use for the display.  The display mode includes
+     * preference for resolution and refresh rate.
+     * If the mode specified is not supported by the display, then no mode change occurs.
+     *
+     * @param mode The {@link Display.Mode} to set, which can include resolution and/or
+     * refresh-rate. It is created using {@link Display.Mode.Builder}.
+     *`
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
+    public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) {
+        // Create a new object containing default values for the unused fields like mode ID and
+        // alternative refresh rates.
+        Display.Mode preferredMode = new Display.Mode(mode.getPhysicalWidth(),
+                mode.getPhysicalHeight(), mode.getRefreshRate());
+        mGlobal.setUserPreferredDisplayMode(mDisplayId, preferredMode);
+    }
+
+    /**
+     * Removes the display's user preferred display mode.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
+    public void clearUserPreferredDisplayMode() {
+        mGlobal.setUserPreferredDisplayMode(mDisplayId, null);
+    }
+
+    /**
+     * Returns the display's user preferred display mode.
+     *
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public Display.Mode getUserPreferredDisplayMode() {
+        return mGlobal.getUserPreferredDisplayMode(mDisplayId);
+    }
+
+
+    /**
      * Returns whether this display can be used to display wide color gamut content.
      * This does not necessarily mean the device itself can render wide color gamut
      * content. To ensure wide color gamut content can be produced, refer to
@@ -1710,6 +1772,30 @@
     }
 
     /**
+     * Returns true if the specified width is valid.
+     * @hide
+     */
+    public static boolean isWidthValid(int width) {
+        return width > 0;
+    }
+
+    /**
+     * Returns true if the specified height is valid.
+     * @hide
+     */
+    public static boolean isHeightValid(int height) {
+        return height > 0;
+    }
+
+    /**
+     * Returns true if the specified refresh-rate is valid.
+     * @hide
+     */
+    public static boolean isRefreshRateValid(float refreshRate) {
+        return refreshRate > 0.0f;
+    }
+
+    /**
      * A mode supported by a given display.
      *
      * @see Display#getSupportedModes()
@@ -1846,6 +1932,30 @@
         }
 
         /**
+         * Returns {@code true} if this mode matches the given parameters, if those parameters are
+         * valid.<p>
+         * If resolution (width and height) is valid and refresh-rate is not, the method matches
+         * only resolution.
+         * If refresh-rate is valid and resolution (width and height) is not, the method matches
+         * only refresh-rate.</p>
+         *
+         * @hide
+         */
+        public boolean matchesIfValid(int width, int height, float refreshRate) {
+            if (!isWidthValid(width) && !isHeightValid(height)
+                    && !isRefreshRateValid(refreshRate)) {
+                return false;
+            }
+            if (isWidthValid(width) != isHeightValid(height)) {
+                return false;
+            }
+            return (!isWidthValid(width) || mWidth == width)
+                    && (!isHeightValid(height) || mHeight == height)
+                    && (!isRefreshRateValid(refreshRate)
+                    || Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate));
+        }
+
+        /**
          * Returns {@code true} if this mode equals to the other mode in all parameters except
          * the refresh rate.
          *
@@ -1855,6 +1965,24 @@
             return mWidth == other.mWidth && mHeight == other.mHeight;
         }
 
+        /**
+         * Returns {@code true} if refresh-rate is set for a display mode
+         *
+         * @hide
+         */
+        public boolean isRefreshRateSet() {
+            return mRefreshRate != INVALID_DISPLAY_REFRESH_RATE;
+        }
+
+        /**
+         * Returns {@code true} if refresh-rate is set for a display mode
+         *
+         * @hide
+         */
+        public boolean isResolutionSet() {
+            return mWidth != INVALID_DISPLAY_WIDTH && mHeight != INVALID_DISPLAY_HEIGHT;
+        }
+
         @Override
         public boolean equals(@Nullable Object other) {
             if (this == other) {
@@ -1923,6 +2051,80 @@
                 return new Mode[size];
             }
         };
+
+        /**
+         * Builder is used to create {@link Display.Mode} objects
+         *
+         * @hide
+         */
+        @TestApi
+        public static final class Builder {
+            private int mWidth;
+            private int mHeight;
+            private float mRefreshRate;
+
+            public Builder() {
+                mWidth = Display.INVALID_DISPLAY_WIDTH;
+                mHeight = Display.INVALID_DISPLAY_HEIGHT;
+                mRefreshRate = Display.INVALID_DISPLAY_REFRESH_RATE;
+            }
+
+            /**
+             * Sets the resolution (width and height) of a {@link Display.Mode}
+             *
+             * @return Instance of {@link Builder}
+             */
+            @NonNull
+            public Builder setResolution(int width, int height) {
+                if (width > 0 && height > 0) {
+                    mWidth = width;
+                    mHeight = height;
+                }
+                return this;
+            }
+
+            /**
+             * Sets the refresh rate of a {@link Display.Mode}
+             *
+             * @return Instance of {@link Builder}
+             */
+            @NonNull
+            public Builder setRefreshRate(float refreshRate) {
+                if (refreshRate > 0.0f) {
+                    mRefreshRate = refreshRate;
+                }
+                return this;
+            }
+
+            /**
+             * Creates the {@link Display.Mode} object.
+             *
+             * <p>
+             * If resolution needs to be set, but refresh-rate doesn't matter, create a mode with
+             * Builder and call setResolution.
+             * {@code
+             * Display.Mode mode =
+             *      new Display.Mode.Builder()
+             *      .setResolution(width, height)
+             *      .build();
+             * }
+             * </p><p>
+             * If refresh-rate needs to be set, but resolution doesn't matter, create a mode with
+             * Builder and call setRefreshRate.
+             * {@code
+             * Display.Mode mode =
+             *      new Display.Mode.Builder()
+             *      .setRefreshRate(refreshRate)
+             *      .build();
+             * }
+             * </p>
+             */
+            @NonNull
+            public Mode build() {
+                Display.Mode mode = new Mode(mWidth, mHeight, mRefreshRate);
+                return mode;
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index ae323226..9889eaa 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -1249,4 +1249,119 @@
             return String.valueOf(mInner);
         }
     }
+
+    /**
+     * A Builder class to construct a DisplayCutout instance.
+     *
+     * <p>Note that this is only for tests purpose. For production code, developers should always
+     * use a {@link DisplayCutout} obtained from the system.</p>
+     */
+    public static final class Builder {
+        private Insets mSafeInsets = Insets.NONE;
+        private Insets mWaterfallInsets = Insets.NONE;
+        private Path mCutoutPath;
+        private final Rect mBoundingRectLeft = new Rect();
+        private final Rect mBoundingRectTop = new Rect();
+        private final Rect mBoundingRectRight = new Rect();
+        private final Rect mBoundingRectBottom = new Rect();
+
+        /**
+         * Begin building a DisplayCutout.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Construct a new {@link DisplayCutout} with the set parameters.
+         */
+        @NonNull
+        public DisplayCutout build() {
+            final CutoutPathParserInfo info;
+            if (mCutoutPath != null) {
+                // Create a fake CutoutPathParserInfo and set it to sCachedCutoutPathParserInfo so
+                // that when getCutoutPath() is called, it will return the cached Path.
+                info = new CutoutPathParserInfo(0, 0, 0, "test", 0, 1f);
+                synchronized (CACHE_LOCK) {
+                    DisplayCutout.sCachedCutoutPathParserInfo = info;
+                    DisplayCutout.sCachedCutoutPath = mCutoutPath;
+                }
+            } else {
+                info = null;
+            }
+            return new DisplayCutout(mSafeInsets.toRect(), mWaterfallInsets, mBoundingRectLeft,
+                    mBoundingRectTop, mBoundingRectRight, mBoundingRectBottom, info, false);
+        }
+
+        /**
+         * Set the safe insets. If not set, the default value is {@link Insets#NONE}.
+         */
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setSafeInsets(@NonNull Insets safeInsets) {
+            mSafeInsets = safeInsets;
+            return this;
+        }
+
+        /**
+         * Set the waterfall insets of the DisplayCutout. If not set, the default value is
+         * {@link Insets#NONE}
+         */
+        @NonNull
+        public Builder setWaterfallInsets(@NonNull Insets waterfallInsets) {
+            mWaterfallInsets = waterfallInsets;
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the left of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectLeft(@NonNull Rect boundingRectLeft) {
+            mBoundingRectLeft.set(boundingRectLeft);
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the top of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectTop(@NonNull Rect boundingRectTop) {
+            mBoundingRectTop.set(boundingRectTop);
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the right of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectRight(@NonNull Rect boundingRectRight) {
+            mBoundingRectRight.set(boundingRectRight);
+            return this;
+        }
+
+        /**
+         * Set a bounding rectangle for a non-functional area on the display which is located on
+         * the bottom of the screen. If not set, the default value is an empty rectangle.
+         */
+        @NonNull
+        public Builder setBoundingRectBottom(@NonNull Rect boundingRectBottom) {
+            mBoundingRectBottom.set(boundingRectBottom);
+            return this;
+        }
+
+        /**
+         * Set the cutout {@link Path}.
+         *
+         * Note that not support creating/testing multiple display cutouts with setCutoutPath() in
+         * parallel.
+         */
+        @NonNull
+        public Builder setCutoutPath(@NonNull Path cutoutPath) {
+            mCutoutPath = cutoutPath;
+            return this;
+        }
+    }
 }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5c08632..774bab4 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -138,13 +138,28 @@
     }
 
     static final class VsyncEventData {
-        // The frame timeline vsync id, used to correlate a frame
-        // produced by HWUI with the timeline data stored in Surface Flinger.
-        public final long id;
 
-        // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
-        // allotted for the frame to be completed.
-        public final long frameDeadline;
+        static final FrameTimeline[] INVALID_FRAME_TIMELINES =
+                {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
+
+        public static class FrameTimeline {
+            FrameTimeline(long vsyncId, long expectedPresentTime, long deadline) {
+                this.vsyncId = vsyncId;
+                this.expectedPresentTime = expectedPresentTime;
+                this.deadline = deadline;
+            }
+
+            // The frame timeline vsync id, used to correlate a frame
+            // produced by HWUI with the timeline data stored in Surface Flinger.
+            public final long vsyncId;
+
+            // The frame timestamp for when the frame is expected to be presented.
+            public final long expectedPresentTime;
+
+            // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
+            // allotted for the frame to be completed.
+            public final long deadline;
+        }
 
         /**
          * The current interval between frames in ns. This will be used to align
@@ -153,16 +168,27 @@
          */
         public final long frameInterval;
 
-        VsyncEventData(long id, long frameDeadline, long frameInterval) {
-            this.id = id;
-            this.frameDeadline = frameDeadline;
+        public final FrameTimeline[] frameTimelines;
+
+        public final int preferredFrameTimelineIndex;
+
+        // Called from native code.
+        @SuppressWarnings("unused")
+        VsyncEventData(FrameTimeline[] frameTimelines, int preferredFrameTimelineIndex,
+                long frameInterval) {
+            this.frameTimelines = frameTimelines;
+            this.preferredFrameTimelineIndex = preferredFrameTimelineIndex;
             this.frameInterval = frameInterval;
         }
 
         VsyncEventData() {
-            this.id = FrameInfo.INVALID_VSYNC_ID;
-            this.frameDeadline = Long.MAX_VALUE;
             this.frameInterval = -1;
+            this.frameTimelines = INVALID_FRAME_TIMELINES;
+            this.preferredFrameTimelineIndex = 0;
+        }
+
+        public FrameTimeline preferredFrameTimeline() {
+            return frameTimelines[preferredFrameTimelineIndex];
         }
     }
 
@@ -256,9 +282,8 @@
     // Called from native code.
     @SuppressWarnings("unused")
     private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
-            long frameTimelineVsyncId, long frameDeadline, long frameInterval) {
-        onVsync(timestampNanos, physicalDisplayId, frame,
-                new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval));
+            VsyncEventData vsyncEventData) {
+        onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl
new file mode 100644
index 0000000..fc9661a
--- /dev/null
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -0,0 +1,28 @@
+/*
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+import android.content.res.Configuration;
+
+/**
+ * API from content embedder back to embedded content in SurfaceControlViewHost
+ * {@hide}
+ */
+oneway interface ISurfaceControlViewHost {
+    void onConfigurationChanged(in Configuration newConfig);
+    void onDispatchDetachedFromWindow();
+}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index adb8b86..0ef5854 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -478,10 +478,15 @@
     public static final int FLAG_IS_GENERATED_GESTURE = 0x8;
 
     /**
-     * This flag associated with {@link #ACTION_POINTER_UP}, this indicates that the pointer
-     * has been canceled. Typically this is used for palm event when the user has accidental
-     * touches.
-     * @hide
+     * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
+     * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
+     * is set, the typical actions that occur in response for a pointer going up (such as click
+     * handlers, end of drawing) should be aborted. This flag is typically set when the user was
+     * accidentally touching the screen, such as by gripping the device, or placing the palm on the
+     * screen.
+     *
+     * @see #ACTION_POINTER_UP
+     * @see #ACTION_CANCEL
      */
     public static final int FLAG_CANCELED = 0x20;
 
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index d160be5..43df294 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -80,6 +80,7 @@
 per-file IPinnedStackListener.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file IRecents*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file IRemote*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ISurfaceControlViewHost*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file IWindow*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RemoteAnimation*.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RemoteAnimation*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index a6c5042d..85a9dbd 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -45,6 +46,35 @@
     private SurfaceControl mSurfaceControl;
     private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
+    private final class ISurfaceControlViewHostImpl extends ISurfaceControlViewHost.Stub {
+        @Override
+        public void onConfigurationChanged(Configuration configuration) {
+            if (mViewRoot == null) {
+                return;
+            }
+            mViewRoot.mHandler.post(() -> {
+                if (mWm != null) {
+                    mWm.setConfiguration(configuration);
+                }
+                if (mViewRoot != null) {
+                    mViewRoot.forceWmRelayout();
+                }
+            });
+        }
+
+        @Override
+        public void onDispatchDetachedFromWindow() {
+            if (mViewRoot == null) {
+                return;
+            }
+            mViewRoot.mHandler.post(() -> {
+                release();
+            });
+        }
+    }
+
+    private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
+
     /**
      * Package encapsulating a Surface hierarchy which contains interactive view
      * elements. It's expected to get this object from
@@ -71,12 +101,14 @@
         private SurfaceControl mSurfaceControl;
         private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
         private final IBinder mInputToken;
+        private final ISurfaceControlViewHost mRemoteInterface;
 
         SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection,
-                       IBinder inputToken) {
+               IBinder inputToken, ISurfaceControlViewHost ri) {
             mSurfaceControl = sc;
             mAccessibilityEmbeddedConnection = connection;
             mInputToken = inputToken;
+            mRemoteInterface = ri;
         }
 
         /**
@@ -97,6 +129,7 @@
             }
             mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection;
             mInputToken = other.mInputToken;
+            mRemoteInterface = other.mRemoteInterface;
         }
 
         private SurfacePackage(Parcel in) {
@@ -105,6 +138,8 @@
             mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
                     in.readStrongBinder());
             mInputToken = in.readStrongBinder();
+            mRemoteInterface = ISurfaceControlViewHost.Stub.asInterface(
+                in.readStrongBinder());
         }
 
         /**
@@ -126,6 +161,13 @@
             return mAccessibilityEmbeddedConnection;
         }
 
+        /**
+         * @hide
+         */
+        public ISurfaceControlViewHost getRemoteInterface() {
+            return mRemoteInterface;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -136,6 +178,7 @@
             mSurfaceControl.writeToParcel(out, flags);
             out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
             out.writeStrongBinder(mInputToken);
+            out.writeStrongBinder(mRemoteInterface.asBinder());
         }
 
         /**
@@ -231,7 +274,7 @@
     public @Nullable SurfacePackage getSurfacePackage() {
         if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
             return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
-                    mViewRoot.getInputToken());
+                mViewRoot.getInputToken(), mRemoteInterface);
         } else {
             return null;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2307a03..41a31e1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8663,15 +8663,34 @@
         if (mAttachInfo == null) {
             return;
         }
-
         RectF position = mAttachInfo.mTmpTransformRect;
-        position.set(0, 0, mRight - mLeft, mBottom - mTop);
-        mapRectFromViewToScreenCoords(position, clipToParent);
+        getBoundsToScreenInternal(position, clipToParent);
         outRect.set(Math.round(position.left), Math.round(position.top),
                 Math.round(position.right), Math.round(position.bottom));
     }
 
     /**
+     * Gets the location of this view in screen coordinates.
+     *
+     * @param outRect The output location
+     * @param clipToParent Whether to clip child bounds to the parent ones.
+     * @hide
+     */
+    public void getBoundsOnScreen(RectF outRect, boolean clipToParent) {
+        if (mAttachInfo == null) {
+            return;
+        }
+        RectF position = mAttachInfo.mTmpTransformRect;
+        getBoundsToScreenInternal(position, clipToParent);
+        outRect.set(position.left, position.top, position.right, position.bottom);
+    }
+
+    private void getBoundsToScreenInternal(RectF position, boolean clipToParent) {
+        position.set(0, 0, mRight - mLeft, mBottom - mTop);
+        mapRectFromViewToScreenCoords(position, clipToParent);
+    }
+
+    /**
      * Map a rectangle from view-relative coordinates to screen-relative coordinates
      *
      * @param rect The rectangle to be mapped
@@ -14248,34 +14267,34 @@
                 hideTooltip();
                 return true;
             }
-        }
-
-        if (action == R.id.accessibilityActionDragDrop) {
-            if (!canAcceptAccessibilityDrop()) {
-                return false;
-            }
-            try {
-                if (mAttachInfo != null && mAttachInfo.mSession != null) {
-                    final int[] location = new int[2];
-                    getLocationInWindow(location);
-                    final int centerX = location[0] + getWidth() / 2;
-                    final int centerY = location[1] + getHeight() / 2;
-                    return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow,
-                            centerX, centerY);
+            case R.id.accessibilityActionDragDrop: {
+                if (!canAcceptAccessibilityDrop()) {
+                    return false;
                 }
-            } catch (RemoteException e) {
-                Log.e(VIEW_LOG_TAG, "Unable to drop for accessibility", e);
-            }
-            return false;
-        } else if (action == R.id.accessibilityActionDragCancel) {
-            if (!startedSystemDragForAccessibility()) {
+                try {
+                    if (mAttachInfo != null && mAttachInfo.mSession != null) {
+                        final int[] location = new int[2];
+                        getLocationInWindow(location);
+                        final int centerX = location[0] + getWidth() / 2;
+                        final int centerY = location[1] + getHeight() / 2;
+                        return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow,
+                                centerX, centerY);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(VIEW_LOG_TAG, "Unable to drop for accessibility", e);
+                }
                 return false;
             }
-            if (mAttachInfo != null && mAttachInfo.mDragToken != null) {
-                cancelDragAndDrop();
-                return true;
+            case R.id.accessibilityActionDragCancel: {
+                if (!startedSystemDragForAccessibility()) {
+                    return false;
+                }
+                if (mAttachInfo != null && mAttachInfo.mDragToken != null) {
+                    cancelDragAndDrop();
+                    return true;
+                }
+                return false;
             }
-            return false;
         }
         return false;
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7718511..7412931 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10639,4 +10639,9 @@
     boolean wasRelayoutRequested() {
         return mRelayoutRequested;
     }
+
+    void forceWmRelayout() {
+       mForceNextWindowRelayout = true;
+       scheduleTraversals();
+    }
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cd9f3eb6..5be3a57 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3581,7 +3581,8 @@
 
         /**
          * If specified, the insets provided by this window will be our window frame minus the
-         * insets specified by providedInternalInsets.
+         * insets specified by providedInternalInsets. This should not be used together with
+         * {@link WindowState#mGivenContentInsets}. If both of them are set, both will be applied.
          *
          * @hide
          */
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d699194d..5176f9b 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -87,7 +87,7 @@
         mHostInputToken = hostInputToken;
     }
 
-    protected void setConfiguration(Configuration configuration) {
+    public void setConfiguration(Configuration configuration) {
         mConfiguration.setTo(configuration);
     }
 
@@ -330,6 +330,7 @@
     public void setInsets(android.view.IWindow window, int touchableInsets,
             android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
             android.graphics.Region touchableRegion) {
+        setTouchRegion(window.asBinder(), touchableRegion);
     }
 
     @Override
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index db7c663..0a33d6c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4321,15 +4321,13 @@
                 return "ACTION_PRESS_AND_HOLD";
             case R.id.accessibilityActionImeEnter:
                 return "ACTION_IME_ENTER";
+            case R.id.accessibilityActionDragStart:
+                return "ACTION_DRAG";
+            case R.id.accessibilityActionDragCancel:
+                return "ACTION_CANCEL_DRAG";
+            case R.id.accessibilityActionDragDrop:
+                return "ACTION_DROP";
             default:
-                // TODO(197520937): Use finalized constants in switch
-                if (action == R.id.accessibilityActionDragStart) {
-                    return "ACTION_DRAG";
-                } else if (action == R.id.accessibilityActionDragCancel) {
-                    return "ACTION_CANCEL_DRAG";
-                } else if (action == R.id.accessibilityActionDragDrop) {
-                    return "ACTION_DROP";
-                }
                 return "ACTION_UNKNOWN";
         }
     }
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 3d68692..3f6a871 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -22,6 +22,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Color;
 import android.graphics.Typeface;
@@ -30,6 +31,8 @@
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 import java.util.Locale;
 
@@ -51,6 +54,7 @@
     private final ArrayList<CaptioningChangeListener> mListeners = new ArrayList<>();
     private final ContentResolver mContentResolver;
     private final ContentObserver mContentObserver;
+    private final Resources mResources;
 
     /**
      * Creates a new captioning manager for the specified context.
@@ -62,6 +66,7 @@
 
         final Handler handler = new Handler(context.getMainLooper());
         mContentObserver = new MyContentObserver(handler);
+        mResources = context.getResources();
     }
 
     /**
@@ -181,6 +186,13 @@
         }
     }
 
+    /**
+     * Returns true if system wide call captioning is enabled for this device.
+     */
+    public boolean isCallCaptioningEnabled() {
+        return mResources.getBoolean(R.bool.config_systemCaptionsServiceCallsEnabled);
+    }
+
     private void notifyEnabledChanged() {
         final boolean enabled = isEnabled();
         synchronized (mListeners) {
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index b1d618e..ab749ee 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -209,6 +209,13 @@
     private boolean mShowWallpaper;
     private boolean mHasRoundedCorners;
 
+    /**
+     * Whether to show a background behind the windows during the animation.
+     * @see #getShowBackground()
+     * @see #setShowBackground(boolean)
+     */
+    private boolean mShowBackground;
+
     private boolean mMore = true;
     private boolean mOneMoreTime = true;
 
@@ -266,6 +273,8 @@
                 a.getBoolean(com.android.internal.R.styleable.Animation_showWallpaper, false));
         setHasRoundedCorners(
                 a.getBoolean(com.android.internal.R.styleable.Animation_hasRoundedCorners, false));
+        setShowBackground(
+                a.getBoolean(com.android.internal.R.styleable.Animation_showBackground, false));
 
         final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
 
@@ -698,6 +707,24 @@
     }
 
     /**
+     * If showBackground is {@code true} and this animation is applied on a window, then the windows
+     * in the animation will animate with the background associated with this window behind them.
+     *
+     * The background comes from the {@link android.R.styleable#Theme_colorBackground} that is
+     * applied to this window through its theme.
+     *
+     * If multiple animating windows have showBackground set to {@code true} during an animation,
+     * the top most window with showBackground set to {@code true} and a valid background color
+     * takes precedence.
+     *
+     * @param showBackground Whether to show a background behind the windows during the animation.
+     * @attr ref android.R.styleable#Animation_showBackground
+     */
+    public void setShowBackground(boolean showBackground) {
+        mShowBackground = showBackground;
+    }
+
+    /**
      * Gets the acceleration curve type for this animation.
      *
      * @return the {@link Interpolator} associated to this animation
@@ -838,6 +865,24 @@
     }
 
     /**
+     * If showBackground is {@code true} and this animation is applied on a window, then the windows
+     * in the animation will animate with the background associated with this window behind them.
+     *
+     * The background comes from the {@link android.R.styleable#Theme_colorBackground} that is
+     * applied to this window through its theme.
+     *
+     * If multiple animating windows have showBackground set to {@code true} during an animation,
+     * the top most window with showBackground set to {@code true} and a valid background color
+     * takes precedence.
+     *
+     * @return if the background of this window should be shown behind the animating windows.
+     * @attr ref android.R.styleable#Animation_showBackground
+     */
+    public boolean getShowBackground() {
+        return mShowBackground;
+    }
+
+    /**
      * <p>Indicates whether or not this animation will affect the transformation
      * matrix. For instance, a fade animation will not affect the matrix whereas
      * a scale animation will.</p>
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index fbc9470..e3d3bfd 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -105,6 +105,13 @@
     private final SparseRectFArray mCharacterBoundsArray;
 
     /**
+     * Container of rectangular position of Editor in the local coordinates that will be transformed
+     * with the transformation matrix when rendered on the screen.
+     * @see {@link EditorBoundsInfo}.
+     */
+    private final EditorBoundsInfo mEditorBoundsInfo;
+
+    /**
      * Transformation matrix that is applied to any positional information of this class to
      * transform local coordinates into screen coordinates.
      */
@@ -141,6 +148,7 @@
         mInsertionMarkerBaseline = source.readFloat();
         mInsertionMarkerBottom = source.readFloat();
         mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
+        mEditorBoundsInfo = source.readTypedObject(EditorBoundsInfo.CREATOR);
         mMatrixValues = source.createFloatArray();
     }
 
@@ -163,6 +171,7 @@
         dest.writeFloat(mInsertionMarkerBaseline);
         dest.writeFloat(mInsertionMarkerBottom);
         dest.writeParcelable(mCharacterBoundsArray, flags);
+        dest.writeTypedObject(mEditorBoundsInfo, flags);
         dest.writeFloatArray(mMatrixValues);
     }
 
@@ -216,6 +225,10 @@
             return false;
         }
 
+        if (!Objects.equals(mEditorBoundsInfo, that.mEditorBoundsInfo)) {
+            return false;
+        }
+
         // Following fields are (partially) covered by hashCode().
 
         if (mComposingTextStart != that.mComposingTextStart
@@ -248,6 +261,7 @@
                 + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
                 + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
                 + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
+                + " mEditorBoundsInfo=" + mEditorBoundsInfo
                 + " mMatrix=" + Arrays.toString(mMatrixValues)
                 + "}";
     }
@@ -266,6 +280,7 @@
         private float mInsertionMarkerBottom = Float.NaN;
         private int mInsertionMarkerFlags = 0;
         private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
+        private EditorBoundsInfo mEditorBoundsInfo = null;
         private float[] mMatrixValues = null;
         private boolean mMatrixInitialized = false;
 
@@ -356,6 +371,17 @@
         }
 
         /**
+         * Sets the current editor related bounds.
+         *
+         * @param bounds {@link EditorBoundsInfo} in local coordinates.
+         */
+        @NonNull
+        public Builder setEditorBoundsInfo(@Nullable EditorBoundsInfo bounds) {
+            mEditorBoundsInfo = bounds;
+            return this;
+        }
+
+        /**
          * Sets the matrix that transforms local coordinates into screen coordinates.
          * @param matrix transformation matrix from local coordinates into screen coordinates. null
          * is interpreted as an identity matrix.
@@ -410,6 +436,7 @@
             if (mCharacterBoundsArrayBuilder != null) {
                 mCharacterBoundsArrayBuilder.reset();
             }
+            mEditorBoundsInfo = null;
         }
     }
 
@@ -425,6 +452,7 @@
         mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
         mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null
                 ? builder.mCharacterBoundsArrayBuilder.build() : null;
+        mEditorBoundsInfo = builder.mEditorBoundsInfo;
         mMatrixValues = new float[9];
         if (builder.mMatrixInitialized) {
             System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
@@ -547,6 +575,14 @@
     }
 
     /**
+     * Returns {@link EditorBoundsInfo} editor related bounds.
+     */
+    @Nullable
+    public EditorBoundsInfo getEditorBoundsInfo() {
+        return mEditorBoundsInfo;
+    }
+
+    /**
      * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
      * matrix that is to be applied other positional data in this class.
      * @return a new instance (copy) of the transformation matrix.
diff --git a/core/java/android/view/inputmethod/EditorBoundsInfo.java b/core/java/android/view/inputmethod/EditorBoundsInfo.java
new file mode 100644
index 0000000..081dc81
--- /dev/null
+++ b/core/java/android/view/inputmethod/EditorBoundsInfo.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Container of rectangular position related info for the Editor.
+ */
+public final class EditorBoundsInfo implements Parcelable {
+
+    /**
+     * Container of rectangular position of Editor in the current window.
+     */
+    private final RectF mEditorBounds;
+
+    /**
+     * Container of rectangular position of Editor with additional padding around for initiating
+     * Stylus Handwriting in the current window.
+     */
+    private final RectF mHandwritingBounds;
+
+    private final int mHashCode;
+
+    private EditorBoundsInfo(@NonNull Parcel source) {
+        mHashCode = source.readInt();
+        mEditorBounds = source.readTypedObject(RectF.CREATOR);
+        mHandwritingBounds = source.readTypedObject(RectF.CREATOR);
+    }
+
+    /**
+     * Returns the bounds of the Editor in local coordinates.
+     *
+     * Screen coordinates can be obtained by transforming with the
+     * {@link CursorAnchorInfo#getMatrix} of the containing {@code CursorAnchorInfo}.
+     */
+    @Nullable
+    public RectF getEditorBounds() {
+        return mEditorBounds;
+    }
+
+    /**
+     * Returns the bounds of the area that should be considered for initiating stylus handwriting
+     * in local coordinates.
+     *
+     * Screen coordinates can be obtained by transforming with the
+     * {@link CursorAnchorInfo#getMatrix} of the containing {@code CursorAnchorInfo}.
+     */
+    @Nullable
+    public RectF getHandwritingBounds() {
+        return mHandwritingBounds;
+    }
+
+    @Override
+    public int hashCode() {
+        return mHashCode;
+    }
+
+    @Override
+    public String toString() {
+        return "EditorBoundsInfo{mEditorBounds=" + mEditorBounds
+                + " mHandwritingBounds=" + mHandwritingBounds
+                + "}";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        EditorBoundsInfo bounds;
+        if (obj instanceof  EditorBoundsInfo) {
+            bounds = (EditorBoundsInfo) obj;
+        } else {
+            return false;
+        }
+        return Objects.equals(bounds.mEditorBounds, mEditorBounds)
+                && Objects.equals(bounds.mHandwritingBounds, mHandwritingBounds);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mHashCode);
+        dest.writeTypedObject(mEditorBounds, flags);
+        dest.writeTypedObject(mHandwritingBounds, flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<EditorBoundsInfo> CREATOR
+            = new Parcelable.Creator<EditorBoundsInfo>() {
+        @Override
+        public EditorBoundsInfo createFromParcel(@NonNull Parcel source) {
+            return new EditorBoundsInfo(source);
+        }
+
+        @Override
+        public EditorBoundsInfo[] newArray(int size) {
+            return new EditorBoundsInfo[size];
+        }
+    };
+
+    /**
+     * Builder for {@link CursorAnchorInfo}.
+     */
+    public static final class Builder {
+        private RectF mEditorBounds = null;
+        private RectF mHandwritingBounds = null;
+
+        /**
+         * Sets the bounding box of the current editor.
+         *
+         * @param bounds {@link RectF} in local coordinates.
+         */
+        @NonNull
+        public EditorBoundsInfo.Builder setEditorBounds(@Nullable RectF bounds) {
+            mEditorBounds = bounds;
+            return this;
+        }
+
+        /**
+         * Sets the current editor's bounds with padding for handwriting.
+         *
+         * @param bounds {@link RectF} in local coordinates.
+         */
+        @NonNull
+        public EditorBoundsInfo.Builder setHandwritingBounds(@Nullable RectF bounds) {
+            mHandwritingBounds = bounds;
+            return this;
+        }
+
+        /**
+         * Returns {@link EditorBoundsInfo} using parameters in this
+         *   {@link EditorBoundsInfo.Builder}.
+         */
+        @NonNull
+        public EditorBoundsInfo build() {
+            return new EditorBoundsInfo(this);
+        }
+    }
+
+    private EditorBoundsInfo(final EditorBoundsInfo.Builder builder) {
+        mEditorBounds = builder.mEditorBounds;
+        mHandwritingBounds = builder.mHandwritingBounds;
+
+        int hash = Objects.hashCode(mEditorBounds);
+        hash *= 31;
+        hash += Objects.hashCode(mHandwritingBounds);
+        mHashCode = hash;
+    }
+}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6284bc2..ac28c31 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -115,6 +115,7 @@
 import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorBoundsInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
@@ -4570,6 +4571,12 @@
             mTextView.getLocationOnScreen(mTmpIntOffset);
             mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
             builder.setMatrix(mViewToScreenMatrix);
+            final RectF bounds = new RectF();
+            mTextView.getBoundsOnScreen(bounds, false /* clipToParent */);
+            EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
+            //TODO(b/210039666): add Handwriting bounds once they're available.
+            builder.setEditorBoundsInfo(
+                    boundsBuilder.setEditorBounds(bounds).build());
 
             final float viewportToContentHorizontalOffset =
                     mTextView.viewportToContentHorizontalOffset();
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index c3ef881..3359a41 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
+import android.util.ArraySet;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -81,8 +82,10 @@
 
     /**
      * This is called when the apps that contains running activities on the display has changed.
+     * The running activities refer to the non-finishing activities regardless of they are running
+     * in a process.
      */
-    public void onRunningAppsChanged(int[] runningUids) {}
+    public void onRunningAppsChanged(ArraySet<Integer> runningUids) {}
 
     /** Dump debug data */
     public void dump(String prefix, final PrintWriter pw) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 915c8fb..fd1e848 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -36,6 +36,7 @@
 import static android.view.WindowManager.TransitionType;
 import static android.view.WindowManager.transitTypeToString;
 
+import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -366,6 +367,7 @@
         private int mStartRotation = ROTATION_UNDEFINED;
         private int mEndRotation = ROTATION_UNDEFINED;
         private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        private @ColorInt int mBackgroundColor;
 
         public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
             mContainer = container;
@@ -387,6 +389,7 @@
             mStartRotation = in.readInt();
             mEndRotation = in.readInt();
             mRotationAnimation = in.readInt();
+            mBackgroundColor = in.readInt();
         }
 
         /** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -446,6 +449,11 @@
             mRotationAnimation = anim;
         }
 
+        /** Sets the background color of this change's container. */
+        public void setBackgroundColor(@ColorInt int backgroundColor) {
+            mBackgroundColor = backgroundColor;
+        }
+
         /** @return the container that is changing. May be null if non-remotable (eg. activity) */
         @Nullable
         public WindowContainerToken getContainer() {
@@ -526,6 +534,12 @@
             return mRotationAnimation;
         }
 
+        /** @return get the background color of this change's container. */
+        @ColorInt
+        public int getBackgroundColor() {
+            return mBackgroundColor;
+        }
+
         /** @hide */
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -542,6 +556,7 @@
             dest.writeInt(mStartRotation);
             dest.writeInt(mEndRotation);
             dest.writeInt(mRotationAnimation);
+            dest.writeInt(mBackgroundColor);
         }
 
         @NonNull
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 4ba7ef2..547535d 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -19,9 +19,10 @@
 import static android.window.ConfigurationHelper.isDifferentDisplay;
 import static android.window.ConfigurationHelper.shouldUpdateResources;
 
+import android.annotation.BinderThread;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityThread;
 import android.app.IWindowToken;
 import android.app.ResourcesManager;
 import android.content.Context;
@@ -30,7 +31,9 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IWindowManager;
@@ -71,6 +74,8 @@
 
     private boolean mAttachToWindowContainer;
 
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
     /**
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
      * can only attach one {@link Context}.
@@ -132,7 +137,8 @@
             if (configuration == null) {
                 return false;
             }
-            onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+            mHandler.post(() -> onConfigurationChanged(configuration, displayId,
+                    false /* shouldReportConfigChange */));
             mAttachToWindowContainer = true;
             return true;
         } catch (RemoteException e) {
@@ -179,9 +185,11 @@
      * @param newConfig the updated {@link Configuration}
      * @param newDisplayId the updated {@link android.view.Display} ID
      */
+    @BinderThread
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
-        onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+        mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId,
+                true /* shouldReportConfigChange */));
     }
 
     // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
@@ -192,6 +200,7 @@
      * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
      * whether to dispatch configuration update or not.
      */
+    @MainThread
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
             boolean shouldReportConfigChange) {
@@ -217,16 +226,14 @@
 
             if (shouldReportConfigChange && context instanceof WindowContext) {
                 final WindowContext windowContext = (WindowContext) context;
-                ActivityThread.currentActivityThread().getHandler().post(
-                        () -> windowContext.dispatchConfigurationChanged(newConfig));
+                windowContext.dispatchConfigurationChanged(newConfig);
             }
 
             final int diff = mConfiguration.diffPublicOnly(newConfig);
             if (shouldReportConfigChange && diff != 0
                     && context instanceof WindowProviderService) {
                 final WindowProviderService windowProviderService = (WindowProviderService) context;
-                ActivityThread.currentActivityThread().getHandler().post(
-                        () -> windowProviderService.onConfigurationChanged(newConfig));
+                windowProviderService.onConfigurationChanged(newConfig);
             }
             freeTextLayoutCachesIfNeeded(diff);
             if (mShouldDumpConfigForIme) {
@@ -248,12 +255,15 @@
         }
     }
 
+    @BinderThread
     @Override
     public void onWindowTokenRemoved() {
-        final Context context = mContextRef.get();
-        if (context != null) {
-            context.destroy();
-            mContextRef.clear();
-        }
+        mHandler.post(() -> {
+            final Context context = mContextRef.get();
+            if (context != null) {
+                context.destroy();
+                mContextRef.clear();
+            }
+        });
     }
 }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index d14054d..e1a67d8 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -24,8 +24,6 @@
 import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
 import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
 
-import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
-import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
 
@@ -241,7 +239,6 @@
             if (!mSurfaceOnly) {
                 mRendererWrapper.addObserver(mObserver);
             }
-            notifyCujEvent(ACTION_SESSION_BEGIN);
         }
     }
 
@@ -523,7 +520,6 @@
                     maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
                     missedSfFramesCount,
                     missedAppFramesCount);
-            notifyCujEvent(ACTION_METRICS_LOGGED);
         }
         if (DEBUG) {
             Log.i(TAG, "finish: CUJ=" + mSession.getName()
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index a33b2f1..5a66e9a 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,10 +16,7 @@
 
 package com.android.internal.jank;
 
-import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
-
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
-import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
 import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
@@ -65,17 +62,16 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Build;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
-import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
 import android.util.Log;
@@ -131,14 +127,8 @@
     private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
-    public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
     public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
-    public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
-    public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
-    public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
-    @VisibleForTesting
-    public static final String PROP_NOTIFY_CUJ_EVENT = "debug.jank.notify_cuj_events";
 
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
@@ -185,6 +175,7 @@
     public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
     public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
     public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
+    public static final int CUJ_UNFOLD_ANIM = 44;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -237,6 +228,7 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -301,6 +293,7 @@
             CUJ_SCREEN_OFF_SHOW_AOD,
             CUJ_ONE_HANDED_ENTER_TRANSITION,
             CUJ_ONE_HANDED_EXIT_TRANSITION,
+            CUJ_UNFOLD_ANIM,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -374,8 +367,7 @@
                 new ChoreographerWrapper(Choreographer.getInstance());
 
         synchronized (mLock) {
-            FrameTrackerListener eventsListener =
-                    (s, act) -> handleCujEvents(config.getContext(), act, s);
+            FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s);
             return new FrameTracker(session, mWorker.getThreadHandler(),
                     threadedRenderer, viewRoot, surfaceControl, choreographer, mMetrics,
                     mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
@@ -383,24 +375,13 @@
         }
     }
 
-    private void handleCujEvents(Context context, String action, Session session) {
+    private void handleCujEvents(String action, Session session) {
         // Clear the running and timeout tasks if the end / cancel was fired within the tracker.
         // Or we might have memory leaks.
         if (needRemoveTasks(action, session)) {
             removeTimeout(session.getCuj());
             removeTracker(session.getCuj());
         }
-
-        // Notify the receivers if necessary.
-        if (session.shouldNotify()) {
-            if (context != null) {
-                notifyEvents(context, action, session);
-            } else {
-                throw new IllegalArgumentException(
-                        "Can't notify cuj events due to lack of context: cuj="
-                        + session.getName() + ", action=" + action);
-            }
-        }
     }
 
     private boolean needRemoveTasks(String action, Session session) {
@@ -412,22 +393,6 @@
         return badEnd || badCancel;
     }
 
-    /**
-     * Notifies who may interest in some CUJ events.
-     */
-    @VisibleForTesting
-    public void notifyEvents(Context context, String action, Session session) {
-        if (action.equals(ACTION_SESSION_CANCEL)
-                && session.getReason() == REASON_CANCEL_NOT_BEGUN) {
-            return;
-        }
-        Intent intent = new Intent(action);
-        intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
-        intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
-        intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
-        context.sendBroadcast(intent);
-    }
-
     private void removeTimeout(@CujType int cujType) {
         synchronized (mLock) {
             Runnable timeout = mTimeoutActions.get(cujType);
@@ -625,7 +590,17 @@
      */
     public static String getNameOfInteraction(int interactionType) {
         // There is an offset amount of 1 between cujType and interactionType.
-        return getNameOfCuj(interactionType - 1);
+        return getNameOfCuj(getCujTypeFromInteraction(interactionType));
+    }
+
+    /**
+     * A helper method to translate interaction type to CUJ type.
+     *
+     * @param interactionType the interaction type defined in AtomsProto.java
+     * @return the integer in {@link CujType}
+     */
+    private static int getCujTypeFromInteraction(int interactionType) {
+        return interactionType - 1;
     }
 
     /**
@@ -724,6 +699,8 @@
                 return "ONE_HANDED_ENTER_TRANSITION";
             case CUJ_ONE_HANDED_EXIT_TRANSITION:
                 return "ONE_HANDED_EXIT_TRANSITION";
+            case CUJ_UNFOLD_ANIM:
+                return "UNFOLD_ANIM";
         }
         return "UNKNOWN";
     }
@@ -935,13 +912,11 @@
         private final long mTimeStamp;
         @Reasons
         private int mReason = REASON_END_UNKNOWN;
-        private final boolean mShouldNotify;
         private final String mName;
 
         public Session(@CujType int cujType, @NonNull String postfix) {
             mCujType = cujType;
             mTimeStamp = System.nanoTime();
-            mShouldNotify = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
             mName = TextUtils.isEmpty(postfix)
                     ? String.format("J<%s>", getNameOfCuj(mCujType))
                     : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
@@ -981,10 +956,5 @@
         public @Reasons int getReason() {
             return mReason;
         }
-
-        /** Determines if should notify the receivers of cuj events */
-        public boolean shouldNotify() {
-            return mShouldNotify;
-        }
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9429c79..a4183ca 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -164,7 +164,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 205;
+    static final int VERSION = 206;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -214,6 +214,26 @@
     private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
     private static final long MILLISECONDS_IN_YEAR = 365 * 24 * 3600 * 1000L;
 
+    private static final LongCounter ZERO_LONG_COUNTER = new LongCounter() {
+        @Override
+        public long getCountLocked(int which) {
+            return 0;
+        }
+
+        @Override
+        public long getCountForProcessState(int procState) {
+            return 0;
+        }
+
+        @Override
+        public void logState(Printer pw, String prefix) {
+            pw.println(prefix + "mCount=0");
+        }
+    };
+
+    private static final LongCounter[] ZERO_LONG_COUNTER_ARRAY =
+            new LongCounter[]{ZERO_LONG_COUNTER};
+
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
@@ -244,6 +264,7 @@
     private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
             MeasuredEnergyStats.POWER_BUCKET_CPU,
             MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+            MeasuredEnergyStats.POWER_BUCKET_WIFI,
     };
 
     // TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -1742,7 +1763,7 @@
         }
     }
 
-    private static class TimeMultiStateCounter implements TimeBaseObs {
+    private static class TimeMultiStateCounter extends LongCounter implements TimeBaseObs {
         private final TimeBase mTimeBase;
         private final LongMultiStateCounter mCounter;
 
@@ -1794,7 +1815,7 @@
         /**
          * Returns accumulated count for the specified state.
          */
-        public long getCountLocked(int procState) {
+        public long getCountForProcessState(@BatteryConsumer.ProcessState int procState) {
             return mCounter.getCount(procState);
         }
 
@@ -1802,6 +1823,12 @@
             return mCounter.getTotalCount();
         }
 
+        @Override
+        public long getCountLocked(int statsType) {
+            return getTotalCountLocked();
+        }
+
+        @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCounter=" + mCounter);
         }
@@ -1947,6 +1974,14 @@
         }
 
         @Override
+        public long getCountForProcessState(int procState) {
+            if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
+                return getCountLocked(STATS_SINCE_CHARGED);
+            }
+            return 0;
+        }
+
+        @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCount=" + mCount);
         }
@@ -3226,57 +3261,50 @@
 
     public static class ControllerActivityCounterImpl extends ControllerActivityCounter
             implements Parcelable {
-        private final LongSamplingCounter mIdleTimeMillis;
+        private final Clock mClock;
+        private final TimeBase mTimeBase;
+        private int mNumTxStates;
+        private int mProcessState;
+        private TimeMultiStateCounter mIdleTimeMillis;
         private final LongSamplingCounter mScanTimeMillis;
         private final LongSamplingCounter mSleepTimeMillis;
-        private final LongSamplingCounter mRxTimeMillis;
-        private final LongSamplingCounter[] mTxTimeMillis;
+        private TimeMultiStateCounter mRxTimeMillis;
+        private TimeMultiStateCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
         private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs;
 
-        public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
-            mIdleTimeMillis = new LongSamplingCounter(timeBase);
+        public ControllerActivityCounterImpl(Clock clock, TimeBase timeBase, int numTxStates) {
+            mClock = clock;
+            mTimeBase = timeBase;
+            mNumTxStates = numTxStates;
             mScanTimeMillis = new LongSamplingCounter(timeBase);
             mSleepTimeMillis = new LongSamplingCounter(timeBase);
-            mRxTimeMillis = new LongSamplingCounter(timeBase);
-            mTxTimeMillis = new LongSamplingCounter[numTxStates];
-            for (int i = 0; i < numTxStates; i++) {
-                mTxTimeMillis[i] = new LongSamplingCounter(timeBase);
-            }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase);
             mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase);
         }
 
-        public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
-            mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
+        public ControllerActivityCounterImpl(Clock clock, TimeBase timeBase, int numTxStates,
+                Parcel in) {
+            mClock = clock;
+            mTimeBase = timeBase;
+            mNumTxStates = numTxStates;
+            mIdleTimeMillis = readTimeMultiStateCounter(in, timeBase);
             mScanTimeMillis = new LongSamplingCounter(timeBase, in);
             mSleepTimeMillis = new LongSamplingCounter(timeBase, in);
-            mRxTimeMillis = new LongSamplingCounter(timeBase, in);
-            final int recordedTxStates = in.readInt();
-            if (recordedTxStates != numTxStates) {
-                throw new ParcelFormatException("inconsistent tx state lengths");
-            }
+            mRxTimeMillis = readTimeMultiStateCounter(in, timeBase);
+            mTxTimeMillis = readTimeMultiStateCounters(in, timeBase, numTxStates);
 
-            mTxTimeMillis = new LongSamplingCounter[numTxStates];
-            for (int i = 0; i < numTxStates; i++) {
-                mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in);
-            }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
             mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in);
         }
 
         public void readSummaryFromParcel(Parcel in) {
-            mIdleTimeMillis.readSummaryFromParcelLocked(in);
+            mIdleTimeMillis = readTimeMultiStateCounter(in, mTimeBase);
             mScanTimeMillis.readSummaryFromParcelLocked(in);
             mSleepTimeMillis.readSummaryFromParcelLocked(in);
-            mRxTimeMillis.readSummaryFromParcelLocked(in);
-            final int recordedTxStates = in.readInt();
-            if (recordedTxStates != mTxTimeMillis.length) {
-                throw new ParcelFormatException("inconsistent tx state lengths");
-            }
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.readSummaryFromParcelLocked(in);
-            }
+            mRxTimeMillis = readTimeMultiStateCounter(in, mTimeBase);
+            mTxTimeMillis = readTimeMultiStateCounters(in, mTimeBase, mNumTxStates);
+
             mPowerDrainMaMs.readSummaryFromParcelLocked(in);
             mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in);
         }
@@ -3287,52 +3315,98 @@
         }
 
         public void writeSummaryToParcel(Parcel dest) {
-            mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+            writeTimeMultiStateCounter(dest, mIdleTimeMillis);
             mScanTimeMillis.writeSummaryFromParcelLocked(dest);
             mSleepTimeMillis.writeSummaryFromParcelLocked(dest);
-            mRxTimeMillis.writeSummaryFromParcelLocked(dest);
-            dest.writeInt(mTxTimeMillis.length);
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.writeSummaryFromParcelLocked(dest);
-            }
+            writeTimeMultiStateCounter(dest, mRxTimeMillis);
+            writeTimeMultiStateCounters(dest, mTxTimeMillis);
             mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
             mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest);
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            mIdleTimeMillis.writeToParcel(dest);
+            writeTimeMultiStateCounter(dest, mIdleTimeMillis);
             mScanTimeMillis.writeToParcel(dest);
             mSleepTimeMillis.writeToParcel(dest);
-            mRxTimeMillis.writeToParcel(dest);
-            dest.writeInt(mTxTimeMillis.length);
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.writeToParcel(dest);
-            }
+            writeTimeMultiStateCounter(dest, mRxTimeMillis);
+            writeTimeMultiStateCounters(dest, mTxTimeMillis);
             mPowerDrainMaMs.writeToParcel(dest);
             mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
         }
 
+        private TimeMultiStateCounter readTimeMultiStateCounter(Parcel in, TimeBase timeBase) {
+            if (in.readBoolean()) {
+                final TimeMultiStateCounter counter =
+                        new TimeMultiStateCounter(timeBase, in, mClock.elapsedRealtime());
+                if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+                    return counter;
+                }
+            }
+            return null;
+        }
+
+        private void writeTimeMultiStateCounter(Parcel dest, TimeMultiStateCounter counter) {
+            if (counter != null) {
+                dest.writeBoolean(true);
+                counter.writeToParcel(dest);
+            } else {
+                dest.writeBoolean(false);
+            }
+        }
+
+        private TimeMultiStateCounter[] readTimeMultiStateCounters(Parcel in, TimeBase timeBase,
+                int expectedNumCounters) {
+            if (in.readBoolean()) {
+                final int numCounters = in.readInt();
+                boolean valid = (numCounters == expectedNumCounters);
+                // Need to read counters out of the Parcel, even if all or some of them are
+                // invalid.
+                TimeMultiStateCounter[] counters = new TimeMultiStateCounter[numCounters];
+                for (int i = 0; i < numCounters; i++) {
+                    final TimeMultiStateCounter counter =
+                            new TimeMultiStateCounter(timeBase, in, mClock.elapsedRealtime());
+                    if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+                        counters[i] = counter;
+                    } else {
+                        valid = false;
+                    }
+                }
+                if (valid) {
+                    return counters;
+                }
+            }
+            return null;
+        }
+
+        private void writeTimeMultiStateCounters(Parcel dest, TimeMultiStateCounter[] counters) {
+            if (counters != null) {
+                dest.writeBoolean(true);
+                dest.writeInt(counters.length);
+                for (TimeMultiStateCounter counter : counters) {
+                    counter.writeToParcel(dest);
+                }
+            } else {
+                dest.writeBoolean(false);
+            }
+        }
+
         public void reset(boolean detachIfReset, long elapsedRealtimeUs) {
-            mIdleTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
+            resetIfNotNull(mIdleTimeMillis, detachIfReset, elapsedRealtimeUs);
             mScanTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
             mSleepTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
-            mRxTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.reset(detachIfReset, elapsedRealtimeUs);
-            }
+            resetIfNotNull(mRxTimeMillis, detachIfReset, elapsedRealtimeUs);
+            resetIfNotNull(mTxTimeMillis, detachIfReset, elapsedRealtimeUs);
             mPowerDrainMaMs.reset(detachIfReset, elapsedRealtimeUs);
             mMonitoredRailChargeConsumedMaMs.reset(detachIfReset, elapsedRealtimeUs);
         }
 
         public void detach() {
-            mIdleTimeMillis.detach();
+            detachIfNotNull(mIdleTimeMillis);
             mScanTimeMillis.detach();
             mSleepTimeMillis.detach();
-            mRxTimeMillis.detach();
-            for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.detach();
-            }
+            detachIfNotNull(mRxTimeMillis);
+            detachIfNotNull(mTxTimeMillis);
             mPowerDrainMaMs.detach();
             mMonitoredRailChargeConsumedMaMs.detach();
         }
@@ -3342,7 +3416,17 @@
          * milliseconds.
          */
         @Override
-        public LongSamplingCounter getIdleTimeCounter() {
+        public LongCounter getIdleTimeCounter() {
+            if (mIdleTimeMillis == null) {
+                return ZERO_LONG_COUNTER;
+            }
+            return mIdleTimeMillis;
+        }
+
+        private TimeMultiStateCounter getOrCreateIdleTimeCounter() {
+            if (mIdleTimeMillis == null) {
+                mIdleTimeMillis = createTimeMultiStateCounter();
+            }
             return mIdleTimeMillis;
         }
 
@@ -3369,7 +3453,17 @@
          * milliseconds.
          */
         @Override
-        public LongSamplingCounter getRxTimeCounter() {
+        public LongCounter getRxTimeCounter() {
+            if (mRxTimeMillis == null) {
+                return ZERO_LONG_COUNTER;
+            }
+            return mRxTimeMillis;
+        }
+
+        private TimeMultiStateCounter getOrCreateRxTimeCounter() {
+            if (mRxTimeMillis == null) {
+                mRxTimeMillis = createTimeMultiStateCounter();
+            }
             return mRxTimeMillis;
         }
 
@@ -3378,10 +3472,33 @@
          * milliseconds.
          */
         @Override
-        public LongSamplingCounter[] getTxTimeCounters() {
+        public LongCounter[] getTxTimeCounters() {
+            if (mTxTimeMillis == null) {
+                return ZERO_LONG_COUNTER_ARRAY;
+            }
             return mTxTimeMillis;
         }
 
+        private TimeMultiStateCounter[] getOrCreateTxTimeCounters() {
+            if (mTxTimeMillis == null) {
+                mTxTimeMillis = new TimeMultiStateCounter[mNumTxStates];
+                for (int i = 0; i < mNumTxStates; i++) {
+                    mTxTimeMillis[i] = createTimeMultiStateCounter();
+                }
+            }
+            return mTxTimeMillis;
+        }
+
+        private TimeMultiStateCounter createTimeMultiStateCounter() {
+            final long timestampMs = mClock.elapsedRealtime();
+            TimeMultiStateCounter counter = new TimeMultiStateCounter(mTimeBase,
+                    BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
+            counter.setState(mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+                    timestampMs);
+            counter.update(0, timestampMs);
+            return counter;
+        }
+
         /**
          * @return a LongSamplingCounter, measuring power use in milli-ampere milliseconds (mAmS).
          */
@@ -3398,6 +3515,21 @@
         public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() {
             return mMonitoredRailChargeConsumedMaMs;
         }
+
+        private void setState(int processState, long elapsedTimeMs) {
+            mProcessState = processState;
+            if (mIdleTimeMillis != null) {
+                mIdleTimeMillis.setState(processState, elapsedTimeMs);
+            }
+            if (mRxTimeMillis != null) {
+                mRxTimeMillis.setState(processState, elapsedTimeMs);
+            }
+            if (mTxTimeMillis != null) {
+                for (int i = 0; i < mTxTimeMillis.length; i++) {
+                    mTxTimeMillis[i].setState(processState, elapsedTimeMs);
+                }
+            }
+        }
     }
 
     /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
@@ -8234,6 +8366,11 @@
                     mapUidProcessStateToBatteryConsumerProcessState(procState);
             getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
             getMobileRadioActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
+            final ControllerActivityCounterImpl wifiControllerActivity =
+                    getWifiControllerActivity();
+            if (wifiControllerActivity != null) {
+                wifiControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
+            }
             final MeasuredEnergyStats energyStats =
                     getOrCreateMeasuredEnergyStatsIfSupportedLocked();
             if (energyStats != null) {
@@ -8271,7 +8408,7 @@
 
             long activeTime = 0;
             for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
-                activeTime += mCpuActiveTimeMs.getCountLocked(procState);
+                activeTime += mCpuActiveTimeMs.getCountForProcessState(procState);
             }
             return activeTime;
         }
@@ -8283,7 +8420,7 @@
                 return 0;
             }
 
-            return mCpuActiveTimeMs.getCountLocked(procState);
+            return mCpuActiveTimeMs.getCountForProcessState(procState);
         }
 
         @Override
@@ -8576,7 +8713,7 @@
         }
 
         @Override
-        public ControllerActivityCounter getWifiControllerActivity() {
+        public ControllerActivityCounterImpl getWifiControllerActivity() {
             return mWifiControllerActivity;
         }
 
@@ -8592,24 +8729,24 @@
 
         public ControllerActivityCounterImpl getOrCreateWifiControllerActivityLocked() {
             if (mWifiControllerActivity == null) {
-                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_BT_TX_LEVELS);
+                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
             }
             return mWifiControllerActivity;
         }
 
         public ControllerActivityCounterImpl getOrCreateBluetoothControllerActivityLocked() {
             if (mBluetoothControllerActivity == null) {
-                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_BT_TX_LEVELS);
+                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_BT_TX_LEVELS);
             }
             return mBluetoothControllerActivity;
         }
 
         public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
             if (mModemControllerActivity == null) {
-                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.getNumTxPowerLevels());
+                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels());
             }
             return mModemControllerActivity;
         }
@@ -8745,6 +8882,13 @@
             return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
         }
 
+        @GuardedBy("mBsi")
+        @Override
+        public long getWifiMeasuredBatteryConsumptionUC(int processState) {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI,
+                    processState);
+        }
+
         /**
          * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
          * since last marked. Also sets the mark time for both these timers.
@@ -9341,7 +9485,7 @@
             if (processState == BatteryConsumer.PROCESS_STATE_ANY) {
                 return mMobileRadioActiveTime.getTotalCountLocked();
             } else {
-                return mMobileRadioActiveTime.getCountLocked(processState);
+                return mMobileRadioActiveTime.getCountForProcessState(processState);
             }
         }
 
@@ -10291,22 +10435,22 @@
             }
 
             if (in.readInt() != 0) {
-                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_WIFI_TX_LEVELS, in);
+                mWifiControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS, in);
             } else {
                 mWifiControllerActivity = null;
             }
 
             if (in.readInt() != 0) {
-                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        NUM_BT_TX_LEVELS, in);
+                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, NUM_BT_TX_LEVELS, in);
             } else {
                 mBluetoothControllerActivity = null;
             }
 
             if (in.readInt() != 0) {
-                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.getNumTxPowerLevels(), in);
+                mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mClock,
+                        mBsi.mOnBatteryTimeBase, ModemActivityInfo.getNumTxPowerLevels(), in);
             } else {
                 mModemControllerActivity = null;
             }
@@ -11273,6 +11417,13 @@
 
                 getMobileRadioActiveTimeCounter()
                         .setState(batteryConsumerProcessState, elapsedRealtimeMs);
+
+                final ControllerActivityCounterImpl wifiControllerActivity =
+                        getWifiControllerActivity();
+                if (wifiControllerActivity != null) {
+                    wifiControllerActivity.setState(batteryConsumerProcessState, elapsedRealtimeMs);
+                }
+
                 final MeasuredEnergyStats energyStats =
                         getOrCreateMeasuredEnergyStatsIfSupportedLocked();
                 if (energyStats != null) {
@@ -11681,10 +11832,11 @@
             mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
             mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
         }
-        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
-        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mWifiActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
+                NUM_WIFI_TX_LEVELS);
+        mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS);
-        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 ModemActivityInfo.getNumTxPowerLevels());
         mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
@@ -12613,7 +12765,8 @@
                         continue;
                     }
 
-                    final Uid u = getUidStatsLocked(mapUid(entry.uid), elapsedRealtimeMs, uptimeMs);
+                    final int uid = mapUid(entry.uid);
+                    final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
                     if (entry.rxBytes != 0) {
                         u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
                                 entry.rxPackets);
@@ -12626,8 +12779,7 @@
                         mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
                                 entry.rxPackets);
 
-                        // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
-                        rxPackets.put(u.getUid(), entry.rxPackets);
+                        add(rxPackets, uid, entry.rxPackets);
 
                         // Sum the total number of packets so that the Rx Power can
                         // be evenly distributed amongst the apps.
@@ -12646,8 +12798,7 @@
                         mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
                                 entry.txPackets);
 
-                        // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
-                        txPackets.put(u.getUid(), entry.txPackets);
+                        add(txPackets, uid, entry.txPackets);
 
                         // Sum the total number of packets so that the Tx Power can
                         // be evenly distributed amongst the apps.
@@ -12774,9 +12925,10 @@
 
                         ControllerActivityCounterImpl activityCounter =
                                 uid.getOrCreateWifiControllerActivityLocked();
-                        activityCounter.getRxTimeCounter().addCountLocked(scanRxTimeSinceMarkMs);
-                        activityCounter.getTxTimeCounters()[0].addCountLocked(
-                                scanTxTimeSinceMarkMs);
+                        activityCounter.getOrCreateRxTimeCounter()
+                                .increment(scanRxTimeSinceMarkMs, elapsedRealtimeMs);
+                        activityCounter.getOrCreateTxTimeCounters()[0]
+                                .increment(scanTxTimeSinceMarkMs, elapsedRealtimeMs);
                         leftOverRxTimeMs -= scanRxTimeSinceMarkMs;
                         leftOverTxTimeMs -= scanTxTimeSinceMarkMs;
                     }
@@ -12796,8 +12948,8 @@
                             Slog.d(TAG, "  IdleTime for UID " + uid.getUid() + ": "
                                     + myIdleTimeMs + " ms");
                         }
-                        uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter()
-                                .addCountLocked(myIdleTimeMs);
+                        uid.getOrCreateWifiControllerActivityLocked().getOrCreateIdleTimeCounter()
+                                .increment(myIdleTimeMs, elapsedRealtimeMs);
                     }
 
                     if (uidEstimatedConsumptionMah != null) {
@@ -12822,8 +12974,8 @@
                     if (DEBUG_ENERGY) {
                         Slog.d(TAG, "  TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
                     }
-                    uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0]
-                            .addCountLocked(myTxTimeMs);
+                    uid.getOrCreateWifiControllerActivityLocked().getOrCreateTxTimeCounters()[0]
+                            .increment(myTxTimeMs, elapsedRealtimeMs);
                     if (uidEstimatedConsumptionMah != null) {
                         uidEstimatedConsumptionMah.add(uid.getUid(),
                                 mWifiPowerCalculator.calcPowerFromControllerDataMah(
@@ -12841,8 +12993,8 @@
                     if (DEBUG_ENERGY) {
                         Slog.d(TAG, "  RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
                     }
-                    uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter()
-                            .addCountLocked(myRxTimeMs);
+                    uid.getOrCreateWifiControllerActivityLocked().getOrCreateRxTimeCounter()
+                            .increment(myRxTimeMs, elapsedRealtimeMs);
                     if (uidEstimatedConsumptionMah != null) {
                         uidEstimatedConsumptionMah.add(uid.getUid(),
                                 mWifiPowerCalculator.calcPowerFromControllerDataMah(
@@ -12854,14 +13006,14 @@
 
 
                 // Update WiFi controller stats.
-                mWifiActivity.getRxTimeCounter().addCountLocked(
-                        info.getControllerRxDurationMillis());
-                mWifiActivity.getTxTimeCounters()[0].addCountLocked(
-                        info.getControllerTxDurationMillis());
+                mWifiActivity.getOrCreateRxTimeCounter().increment(
+                        info.getControllerRxDurationMillis(), elapsedRealtimeMs);
+                mWifiActivity.getOrCreateTxTimeCounters()[0].increment(
+                        info.getControllerTxDurationMillis(), elapsedRealtimeMs);
                 mWifiActivity.getScanTimeCounter().addCountLocked(
                         info.getControllerScanDurationMillis());
-                mWifiActivity.getIdleTimeCounter().addCountLocked(
-                        info.getControllerIdleDurationMillis());
+                mWifiActivity.getOrCreateIdleTimeCounter().increment(
+                        info.getControllerIdleDurationMillis(), elapsedRealtimeMs);
 
                 // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
                 final double opVolt = mPowerProfile.getAveragePower(
@@ -12961,14 +13113,16 @@
 
             if (deltaInfo != null) {
                 mHasModemReporting = true;
-                mModemActivity.getIdleTimeCounter().addCountLocked(
-                        deltaInfo.getIdleTimeMillis());
+                mModemActivity.getOrCreateIdleTimeCounter()
+                        .increment(deltaInfo.getIdleTimeMillis(), elapsedRealtimeMs);
                 mModemActivity.getSleepTimeCounter().addCountLocked(
                         deltaInfo.getSleepTimeMillis());
-                mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
+                mModemActivity.getOrCreateRxTimeCounter()
+                        .increment(deltaInfo.getReceiveTimeMillis(), elapsedRealtimeMs);
                 for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
-                    mModemActivity.getTxTimeCounters()[lvl]
-                        .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl));
+                    mModemActivity.getOrCreateTxTimeCounters()[lvl]
+                            .increment(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl),
+                                    elapsedRealtimeMs);
                 }
 
                 // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -13086,7 +13240,8 @@
                             if (totalRxPackets > 0 && entry.rxPackets > 0) {
                                 final long rxMs = (entry.rxPackets
                                     * deltaInfo.getReceiveTimeMillis()) / totalRxPackets;
-                                activityCounter.getRxTimeCounter().addCountLocked(rxMs);
+                                activityCounter.getOrCreateRxTimeCounter()
+                                        .increment(rxMs, elapsedRealtimeMs);
                             }
 
                             if (totalTxPackets > 0 && entry.txPackets > 0) {
@@ -13095,7 +13250,8 @@
                                     long txMs = entry.txPackets
                                             * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
                                     txMs /= totalTxPackets;
-                                    activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
+                                    activityCounter.getOrCreateTxTimeCounters()[lvl]
+                                            .increment(txMs, elapsedRealtimeMs);
                                 }
                             }
                         }
@@ -13193,8 +13349,8 @@
             energy = info.getControllerEnergyUsed();
             if (!info.getUidTraffic().isEmpty()) {
                 for (UidTraffic traffic : info.getUidTraffic()) {
-                    uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
-                    uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
+                    add(uidRxBytes, traffic.getUid(), traffic.getRxBytes());
+                    add(uidTxBytes, traffic.getUid(), traffic.getTxBytes());
                 }
             }
         }
@@ -13317,8 +13473,10 @@
 
                 final ControllerActivityCounterImpl counter =
                         u.getOrCreateBluetoothControllerActivityLocked();
-                counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs);
-                counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs);
+                counter.getOrCreateRxTimeCounter()
+                        .increment(scanTimeRxSinceMarkMs, elapsedRealtimeMs);
+                counter.getOrCreateTxTimeCounters()[0]
+                        .increment(scanTimeTxSinceMarkMs, elapsedRealtimeMs);
 
                 if (uidEstimatedConsumptionMah != null) {
                     uidEstimatedConsumptionMah.add(u.getUid(),
@@ -13385,7 +13543,7 @@
                     if (DEBUG_ENERGY) {
                         Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
                     }
-                    counter.getRxTimeCounter().addCountLocked(timeRxMs);
+                    counter.getOrCreateRxTimeCounter().increment(timeRxMs, elapsedRealtimeMs);
 
                     if (uidEstimatedConsumptionMah != null) {
                         uidEstimatedConsumptionMah.add(u.getUid(),
@@ -13398,7 +13556,8 @@
                     if (DEBUG_ENERGY) {
                         Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
                     }
-                    counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
+                    counter.getOrCreateTxTimeCounters()[0]
+                            .increment(timeTxMs, elapsedRealtimeMs);
 
                     if (uidEstimatedConsumptionMah != null) {
                         uidEstimatedConsumptionMah.add(u.getUid(),
@@ -13408,9 +13567,9 @@
             }
         }
 
-        mBluetoothActivity.getRxTimeCounter().addCountLocked(rxTimeMs);
-        mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(txTimeMs);
-        mBluetoothActivity.getIdleTimeCounter().addCountLocked(idleTimeMs);
+        mBluetoothActivity.getOrCreateRxTimeCounter().increment(rxTimeMs, elapsedRealtimeMs);
+        mBluetoothActivity.getOrCreateTxTimeCounters()[0].increment(txTimeMs, elapsedRealtimeMs);
+        mBluetoothActivity.getOrCreateIdleTimeCounter().increment(idleTimeMs, elapsedRealtimeMs);
 
         // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
         final double opVolt = mPowerProfile.getAveragePower(
@@ -16288,6 +16447,7 @@
     @GuardedBy("this")
     public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
         final int version = in.readInt();
+
         if (version != VERSION) {
             Slog.w("BatteryStats", "readFromParcel: version got " + version
                 + ", expected " + VERSION + "; erasing old stats");
@@ -17447,15 +17607,15 @@
         }
         mWifiActiveTimer = new StopwatchTimer(mClock, null, -900, null,
             mOnBatteryTimeBase, in);
-        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mWifiActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_WIFI_TX_LEVELS, in);
         for (int i=0; i<mGpsSignalQualityTimer.length; i++) {
             mGpsSignalQualityTimer[i] = new StopwatchTimer(mClock, null, -1000 - i,
                 null, mOnBatteryTimeBase, in);
         }
-        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mBluetoothActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS, in);
-        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+        mModemActivity = new ControllerActivityCounterImpl(mClock, mOnBatteryTimeBase,
                 ModemActivityInfo.getNumTxPowerLevels(), in);
         mHasWifiReporting = in.readInt() != 0;
         mHasBluetoothReporting = in.readInt() != 0;
@@ -17980,4 +18140,8 @@
         pw.println();
         dumpMeasuredEnergyStatsLocked(pw);
     }
+
+    private static void add(SparseLongArray array, int key, long delta) {
+        array.put(key, array.get(key) + delta);
+    }
 }
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 3915b0e..2a71ac6 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -34,6 +35,9 @@
 public class WifiPowerCalculator extends PowerCalculator {
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
     private static final String TAG = "WifiPowerCalculator";
+
+    private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
     private final UsageBasedPowerEstimator mIdlePowerEstimator;
     private final UsageBasedPowerEstimator mTxPowerEstimator;
     private final UsageBasedPowerEstimator mRxPowerEstimator;
@@ -51,6 +55,9 @@
         public long wifiTxPackets;
         public long wifiRxBytes;
         public long wifiTxBytes;
+
+        public BatteryConsumer.Key[] keys;
+        public double[] powerPerKeyMah;
     }
 
     public WifiPowerCalculator(PowerProfile profile) {
@@ -77,7 +84,7 @@
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
-
+        BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
         long totalAppDurationMs = 0;
         double totalAppPowerMah = 0;
         final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
@@ -85,9 +92,20 @@
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+            if (keys == UNINITIALIZED_KEYS) {
+                if (query.isProcessStateDataNeeded()) {
+                    keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_WIFI);
+                    powerDurationAndTraffic.keys = keys;
+                    powerDurationAndTraffic.powerPerKeyMah = new double[keys.length];
+                } else {
+                    keys = null;
+                }
+            }
+
             final long consumptionUC =
                     app.getBatteryStatsUid().getWifiMeasuredBatteryConsumptionUC();
             final int powerModel = getPowerModel(consumptionUC, query);
+
             calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), powerModel,
                     rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED,
                     batteryStats.hasWifiActivityReporting(), consumptionUC);
@@ -99,6 +117,20 @@
                     powerDurationAndTraffic.durationMs);
             app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
                     powerDurationAndTraffic.powerMah, powerModel);
+
+            if (query.isProcessStateDataNeeded() && keys != null) {
+                for (int j = 0; j < keys.length; j++) {
+                    BatteryConsumer.Key key = keys[j];
+                    final int processState = key.processState;
+                    if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                        // Already populated with the total across all process states
+                        continue;
+                    }
+
+                    app.setConsumedPower(key, powerDurationAndTraffic.powerPerKeyMah[j],
+                            powerModel);
+                }
+            }
         }
 
         final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
@@ -193,21 +225,23 @@
                 BatteryStats.NETWORK_WIFI_TX_DATA,
                 statsType);
 
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-            powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
-        }
-
         if (hasWifiActivityReporting && mHasWifiPowerController) {
             final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
             if (counter != null) {
-                final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
-                final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
-                final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
+                final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
+                final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
+                final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
+
+                final long rxTime = rxTimeCounter.getCountLocked(statsType);
+                final long txTime = txTimeCounter.getCountLocked(statsType);
+                final long idleTime = idleTimeCounter.getCountLocked(statsType);
 
                 powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
                 if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
                     powerDurationAndTraffic.powerMah
                             = calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
+                } else {
+                    powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
                 }
 
                 if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
@@ -215,9 +249,32 @@
                             + "ms tx=" + txTime + "ms power=" + formatCharge(
                             powerDurationAndTraffic.powerMah));
                 }
+
+                if (powerDurationAndTraffic.keys != null) {
+                    for (int i = 0; i < powerDurationAndTraffic.keys.length; i++) {
+                        final int processState = powerDurationAndTraffic.keys[i].processState;
+                        if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                            continue;
+                        }
+
+                        if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
+                            powerDurationAndTraffic.powerPerKeyMah[i] =
+                                    calcPowerFromControllerDataMah(
+                                            rxTimeCounter.getCountForProcessState(processState),
+                                            txTimeCounter.getCountForProcessState(processState),
+                                            idleTimeCounter.getCountForProcessState(processState));
+                        } else {
+                            powerDurationAndTraffic.powerPerKeyMah[i] =
+                                    uCtoMah(u.getWifiMeasuredBatteryConsumptionUC(processState));
+                        }
+                    }
+                }
             } else {
                 powerDurationAndTraffic.durationMs = 0;
                 powerDurationAndTraffic.powerMah = 0;
+                if (powerDurationAndTraffic.powerPerKeyMah != null) {
+                    Arrays.fill(powerDurationAndTraffic.powerPerKeyMah, 0);
+                }
             }
         } else {
             final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
@@ -233,6 +290,14 @@
                         powerDurationAndTraffic.wifiRxPackets,
                         powerDurationAndTraffic.wifiTxPackets,
                         wifiRunningTime, wifiScanTimeMs, batchTimeMs);
+            } else {
+                powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
+            }
+
+            if (powerDurationAndTraffic.powerPerKeyMah != null) {
+                // Per-process state attribution is not supported in the absence of WiFi
+                // activity reporting
+                Arrays.fill(powerDurationAndTraffic.powerPerKeyMah, 0);
             }
 
             if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
diff --git a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
new file mode 100644
index 0000000..d48b4b1
--- /dev/null
+++ b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util.dump;
+
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Dumpable;
+import android.util.DumpableContainer;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+// TODO(b/149254050): add unit tests
+/**
+ * Helper class for {@link DumpableContainer} implementations - they can "implement it by
+ * association", i.e., by delegating the interface methods to a {@code DumpableContainerImpl}.
+ *
+ * @hide
+ */
+public final class DumpableContainerImpl implements DumpableContainer {
+
+    private static final String TAG = DumpableContainerImpl.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+
+    @Nullable
+    private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
+
+    @Override
+    public boolean addDumpable(Dumpable dumpable) {
+        Objects.requireNonNull(dumpable, "dumpable");
+        String name = dumpable.getDumpableName();
+        Objects.requireNonNull(name, () -> "name of" + dumpable);
+
+        if (mDumpables.containsKey(name)) {
+            Log.e(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+                    + " with that name (" + name + "): " + mDumpables.get(name));
+            return false;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "Adding " + name + " -> " + dumpable);
+        }
+        mDumpables.put(name,  dumpable);
+        return true;
+    }
+
+    /**
+     * Dumps the number of dumpable, without a newline.
+     */
+    private int dumpNumberDumpables(IndentingPrintWriter writer) {
+        int size = mDumpables == null ? 0 : mDumpables.size();
+        if (size == 0) {
+            writer.print("No dumpables");
+        } else {
+            writer.print(size); writer.print(" dumpables");
+        }
+        return size;
+    }
+
+    /**
+     * Lists the name of all dumpables to the given {@code writer}.
+     */
+    public void listDumpables(String prefix, PrintWriter writer) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+
+        int size = dumpNumberDumpables(ipw);
+        if (size == 0) {
+            ipw.println();
+            return;
+        }
+        ipw.print(": ");
+        for (int i = 0; i < size; i++) {
+            ipw.print(mDumpables.keyAt(i));
+            if (i < size - 1) ipw.print(' ');
+        }
+        ipw.println();
+    }
+
+    /**
+     * Dumps the content of all dumpables to the given {@code writer}.
+     */
+    public void dumpAllDumpables(String prefix, PrintWriter writer, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+        int size = dumpNumberDumpables(ipw);
+        if (size == 0) {
+            ipw.println();
+            return;
+        }
+        ipw.println(": ");
+
+        for (int i = 0; i < size; i++) {
+            String dumpableName = mDumpables.keyAt(i);
+            ipw.print('#'); ipw.print(i); ipw.print(": "); ipw.println(dumpableName);
+            Dumpable dumpable = mDumpables.valueAt(i);
+            indentAndDump(ipw, dumpable, args);
+        }
+    }
+
+    private void indentAndDump(IndentingPrintWriter writer, Dumpable dumpable, String[] args) {
+        writer.increaseIndent();
+        try {
+            dumpable.dump(writer, args);
+        } finally {
+            writer.decreaseIndent();
+        }
+    }
+
+    /**
+     * Dumps the content of a specific dumpable to the given {@code writer}.
+     */
+    @SuppressWarnings("resource") // cannot close ipw as it would close writer
+    public void dumpOneDumpable(String prefix, PrintWriter writer, String dumpableName,
+            String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
+        Dumpable dumpable = mDumpables.get(dumpableName);
+        if (dumpable == null) {
+            ipw.print("No "); ipw.println(dumpableName);
+            return;
+        }
+        ipw.print(dumpableName); ipw.println(':');
+        indentAndDump(ipw, dumpable, args);
+    }
+}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index d16d9c6..db4bc2c 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -24,6 +24,8 @@
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.RecoveryCertPath;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 
@@ -96,4 +98,10 @@
     boolean tryUnlockWithCachedUnifiedChallenge(int userId);
     void removeCachedUnifiedChallenge(int userId);
     void updateEncryptionPassword(int type, in byte[] password);
+    boolean registerWeakEscrowTokenRemovedListener(in IWeakEscrowTokenRemovedListener listener);
+    boolean unregisterWeakEscrowTokenRemovedListener(in IWeakEscrowTokenRemovedListener listener);
+    long addWeakEscrowToken(in byte[] token, int userId, in IWeakEscrowTokenActivatedListener callback);
+    boolean removeWeakEscrowToken(long handle, int userId);
+    boolean isWeakEscrowTokenActive(long handle, int userId);
+    boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/core/java/com/android/internal/widget/IWeakEscrowTokenActivatedListener.aidl
similarity index 71%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to core/java/com/android/internal/widget/IWeakEscrowTokenActivatedListener.aidl
index 6041460..9c8d9d6 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/core/java/com/android/internal/widget/IWeakEscrowTokenActivatedListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package com.android.internal.widget;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+/** @hide */
+oneway interface IWeakEscrowTokenActivatedListener {
+    void onWeakEscrowTokenActivated(long handle, int userId);
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/core/java/com/android/internal/widget/IWeakEscrowTokenRemovedListener.aidl
similarity index 71%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to core/java/com/android/internal/widget/IWeakEscrowTokenRemovedListener.aidl
index 6041460..7018048 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/core/java/com/android/internal/widget/IWeakEscrowTokenRemovedListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package com.android.internal.widget;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+/** @hide */
+oneway interface IWeakEscrowTokenRemovedListener {
+    void onWeakEscrowTokenRemoved(long handle, int userId);
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 5a03277..f91776e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1236,6 +1236,28 @@
         }
     }
 
+    /** Register the given WeakEscrowTokenRemovedListener. */
+    public boolean registerWeakEscrowTokenRemovedListener(
+            @NonNull final IWeakEscrowTokenRemovedListener listener) {
+        try {
+            return getLockSettings().registerWeakEscrowTokenRemovedListener(listener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not register WeakEscrowTokenRemovedListener.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Unregister the given WeakEscrowTokenRemovedListener. */
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            @NonNull final IWeakEscrowTokenRemovedListener listener) {
+        try {
+            return getLockSettings().unregisterWeakEscrowTokenRemovedListener(listener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not register WeakEscrowTokenRemovedListener.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
         try {
             getLockSettings().reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
@@ -1355,15 +1377,38 @@
     }
 
     /**
+     * Create a weak escrow token for the current user, which can later be used to unlock FBE
+     * or change user password.
+     *
+     * After adding, if the user currently has lockscreen password, they will need to perform a
+     * confirm credential operation in order to activate the token for future use. If the user
+     * has no secure lockscreen, then the token is activated immediately.
+     *
+     * If the user changes or removes lockscreen password, activated weak escrow tokens will be
+     * removed.
+     *
+     * @return a unique 64-bit token handle which is needed to refer to this token later.
+     */
+    public long addWeakEscrowToken(byte[] token, int userId,
+            @NonNull IWeakEscrowTokenActivatedListener callback) {
+        try {
+            return getLockSettings().addWeakEscrowToken(token, userId, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not add weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Callback interface to notify when an added escrow token has been activated.
      */
     public interface EscrowTokenStateChangeCallback {
         /**
          * The method to be called when the token is activated.
          * @param handle 64 bit handle corresponding to the escrow token
-         * @param userid user for whom the escrow token has been added
+         * @param userId user for whom the escrow token has been added
          */
-        void onEscrowTokenActivated(long handle, int userid);
+        void onEscrowTokenActivated(long handle, int userId);
     }
 
     /**
@@ -1379,6 +1424,21 @@
     }
 
     /**
+     * Remove a weak escrow token.
+     *
+     * @return true if the given handle refers to a valid weak token previously returned from
+     * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+     */
+    public boolean removeWeakEscrowToken(long handle, int userId) {
+        try {
+            return getLockSettings().removeWeakEscrowToken(handle, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not remove the weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check if the given escrow token is active or not. Only active token can be used to call
      * {@link #setLockCredentialWithToken} and {@link #unlockUserWithToken}
      *
@@ -1389,6 +1449,29 @@
     }
 
     /**
+     * Check if the given weak escrow token is active or not. Only active token can be used to call
+     * {@link #setLockCredentialWithToken} and {@link #unlockUserWithToken}
+     */
+    public boolean isWeakEscrowTokenActive(long handle, int userId) {
+        try {
+            return getLockSettings().isWeakEscrowTokenActive(handle, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not check the weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Check if the given weak escrow token is valid. */
+    public boolean isWeakEscrowTokenValid(long handle, byte[] token, int userId) {
+        try {
+            return getLockSettings().isWeakEscrowTokenValid(handle, token, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not validate the weak token.");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Change a user's lock credential with a pre-configured escrow token.
      *
      * <p>This method is only available to code running in the system server process itself.
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index d0504fb..d039bcf 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -66,6 +66,7 @@
     jfieldID    flags;
     //methods
     jmethodID   setType;
+    jmethodID   setId;
     jmethodID   setUuid;
     jmethodID   init;
 } gSensorOffsets;
@@ -112,6 +113,7 @@
     sensorOffsets.flags = GetFieldIDOrDie(_env,sensorClass, "mFlags", "I");
 
     sensorOffsets.setType = GetMethodIDOrDie(_env,sensorClass, "setType", "(I)Z");
+    sensorOffsets.setId = GetMethodIDOrDie(_env,sensorClass, "setId", "(I)V");
     sensorOffsets.setUuid = GetMethodIDOrDie(_env,sensorClass, "setUuid", "(JJ)V");
     sensorOffsets.init = GetMethodIDOrDie(_env,sensorClass, "<init>", "()V");
 
@@ -188,9 +190,10 @@
             env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
         }
 
-        // TODO(b/29547335): Rename "setUuid" method to "setId".
-        int64_t id = nativeSensor.getId();
-        env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, 0);
+        int32_t id = nativeSensor.getId();
+        env->CallVoidMethod(sensor, sensorOffsets.setId, id);
+        Sensor::uuid_t uuid = nativeSensor.getUuid();
+        env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, uuid.i64[0], uuid.i64[1]);
     }
     return sensor;
 }
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index ce772cf..d91d526 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -48,6 +48,16 @@
         jmethodID init;
     } frameRateOverrideClassInfo;
 
+    struct {
+        jclass clazz;
+        jmethodID init;
+    } frameTimelineClassInfo;
+
+    struct {
+        jclass clazz;
+        jmethodID init;
+    } vsyncEventDataClassInfo;
+
 } gDisplayEventReceiverClassInfo;
 
 
@@ -105,9 +115,38 @@
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
+
+        ScopedLocalRef<jobjectArray>
+                frameTimelineObjs(env,
+                                  env->NewObjectArray(vsyncEventData.frameTimelines.size(),
+                                                      gDisplayEventReceiverClassInfo
+                                                              .frameTimelineClassInfo.clazz,
+                                                      /*initial element*/ NULL));
+        for (int i = 0; i < vsyncEventData.frameTimelines.size(); i++) {
+            VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
+            ScopedLocalRef<jobject>
+                    frameTimelineObj(env,
+                                     env->NewObject(gDisplayEventReceiverClassInfo
+                                                            .frameTimelineClassInfo.clazz,
+                                                    gDisplayEventReceiverClassInfo
+                                                            .frameTimelineClassInfo.init,
+                                                    frameTimeline.id,
+                                                    frameTimeline.expectedPresentTime,
+                                                    frameTimeline.deadlineTimestamp));
+            env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get());
+        }
+        ScopedLocalRef<jobject>
+                vsyncEventDataJava(env,
+                                   env->NewObject(gDisplayEventReceiverClassInfo
+                                                          .vsyncEventDataClassInfo.clazz,
+                                                  gDisplayEventReceiverClassInfo
+                                                          .vsyncEventDataClassInfo.init,
+                                                  frameTimelineObjs.get(),
+                                                  vsyncEventData.preferredFrameTimelineIndex,
+                                                  vsyncEventData.frameInterval));
+
         env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
-                            timestamp, displayId.value, count, vsyncEventData.id,
-                            vsyncEventData.deadlineTimestamp, vsyncEventData.frameInterval);
+                            timestamp, displayId.value, count, vsyncEventDataJava.get());
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -239,7 +278,7 @@
 
     gDisplayEventReceiverClassInfo.dispatchVsync =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
-                             "(JJIJJJ)V");
+                             "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
     gDisplayEventReceiverClassInfo.dispatchModeChanged =
@@ -258,6 +297,24 @@
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz,
                              "<init>", "(IF)V");
 
+    jclass frameTimelineClazz =
+            FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData$FrameTimeline");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz =
+            MakeGlobalRefOrDie(env, frameTimelineClazz);
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                             "<init>", "(JJJ)V");
+
+    jclass vsyncEventDataClazz =
+            FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData");
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz =
+            MakeGlobalRefOrDie(env, vsyncEventDataClazz);
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.init =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                             "<init>",
+                             "([Landroid/view/"
+                             "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V");
+
     return res;
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 713cb36..d0b9f88 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -716,6 +716,7 @@
 
     <!-- Added in T -->
     <protected-broadcast android:name="android.intent.action.REFRESH_SAFETY_SOURCES" />
+    <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -3595,6 +3596,18 @@
     <permission android:name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"
         android:protectionLevel="signature|privileged" />
 
+    <!-- ========================================= -->
+    <!-- Permissions for SupplementalApi -->
+    <!-- ========================================= -->
+    <eat-comment />
+
+    <!-- TODO(b/213488783): Update with correct names. -->
+    <!-- Allows an application to access SupplementalApis. -->
+    <permission android:name="android.permission.ACCESS_SUPPLEMENTAL_APIS"
+        android:label="@string/permlab_accessSupplementalApi"
+        android:description="@string/permdesc_accessSupplementalApi"
+        android:protectionLevel="normal" />
+
     <!-- ==================================== -->
     <!-- Private permissions                  -->
     <!-- ==================================== -->
@@ -5334,6 +5347,12 @@
     <permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to manage weak escrow token on the device. This permission
+         is not available to third party applications.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_WEAK_ESCROW_TOKEN"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to listen to trust changes.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.TRUST_LISTENER"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2267781..a08bca3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6884,6 +6884,9 @@
         <!-- Special option for window animations: whether window should have rounded corners.
              @see ScreenDecorationsUtils#getWindowCornerRadius(Resources) -->
         <attr name="hasRoundedCorners" format="boolean" />
+        <!-- Special option for window animations: whether the window's background should be used as
+             a background to the animation. -->
+        <attr name="showBackground" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="AnimationSet">
@@ -9665,4 +9668,11 @@
     <attr name="iconfactoryBadgeSize" format="dimension"/>
     <!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
     <attr name="lStar" format="float"/>
-</resources>
+
+    <!-- The attributes of the {@code <locale>} tag within {@code <locale-config>}. -->
+    <declare-styleable name="LocaleConfig_Locale">
+        <!-- The <a href="https://www.rfc-editor.org/rfc/bcp/bcp47.txt">IETF BCP47 language tag</a>
+        of the supported locale. {@link android.app.LocaleConfig} -->
+        <attr name="name" />
+    </declare-styleable>
+    </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index db24475..a595433 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1519,6 +1519,9 @@
     <!-- An XML resource with the application's Network Security Config. -->
     <attr name="networkSecurityConfig" format="reference" />
 
+    <!-- An XML resource with the application's {@link android.app.LocaleConfig} -->
+    <attr name="localeConfig" format="reference" />
+
     <!-- When an application is partitioned into splits, this is the name of the
          split that contains the defined component. -->
     <attr name="splitName" format="string" />
@@ -1801,6 +1804,7 @@
         <attr name="maxAspectRatio" />
         <attr name="minAspectRatio" />
         <attr name="networkSecurityConfig" />
+        <attr name="localeConfig" />
         <!-- Declare the category of this app. Categories are used to cluster multiple apps
              together into meaningful groups, such as when summarizing battery, network, or
              disk usage. Apps should only define this value when they fit well into one of
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5fa7409..e801ac0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3253,6 +3253,8 @@
     <public name="showClockAndComplications" />
     <!-- @hide @SystemApi -->
     <public name="gameSessionService" />
+    <public name="localeConfig" />
+    <public name="showBackground" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
@@ -3317,8 +3319,6 @@
   </staging-public-group>
 
   <staging-public-group type="bool" first-id="0x01cf0000">
-    <!-- @hide @SystemApi -->
-    <public name="config_systemCaptionsServiceCallsEnabled" />
     <!-- @hide @TestApi -->
     <public name="config_preventImeStartupUnlessTextEditor" />
   </staging-public-group>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2879759..6577ebc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4046,11 +4046,16 @@
     <!-- Description of an application permission that lets it ask user to ignore battery optimizations for that app-->
     <string name="permdesc_requestIgnoreBatteryOptimizations">Allows an app to ask for permission to ignore battery optimizations for that app.</string>
 
-    <!-- Title of an application permission that lets query all other packages. [CHAR LIMIT=NONE] -->
+    <!-- Title of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
     <string name="permlab_queryAllPackages">query all packages</string>
     <!-- Description of an application permission that lets it query all other packages. [CHAR LIMIT=NONE] -->
     <string name="permdesc_queryAllPackages">Allows an app to see all installed packages.</string>
 
+    <!-- Title of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE] -->
+    <string name="permlab_accessSupplementalApi">access SupplementalApis</string>
+    <!-- Description of an application permission that lets it access SupplementalApis. [CHAR LIMIT=NONE]-->
+    <string name="permdesc_accessSupplementalApi">Allows an application to access SupplementalApis.</string>
+
     <!-- Shown in the tutorial for tap twice for zoom control. -->
     <string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
 
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index dcfca84..3b2f244 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -42,7 +42,6 @@
         <item name="outlineSpotShadowColor">@color/btn_colored_background_material</item>
         <item name="textAppearance">?attr/textAppearanceButton</item>
         <item name="textColor">@color/btn_colored_text_material</item>
-        <item name="drawableTint">@color/btn_colored_text_material</item>
     </style>
     <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView" />
     <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0acd4bf..4e77563 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3654,6 +3654,8 @@
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
+  <java-symbol type="bool" name="config_systemCaptionsServiceCallsEnabled" />
+
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
   <java-symbol type="string" name="foreground_service_apps_in_background" />
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 1b1f64a..bd987a0 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -16,8 +16,7 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.frameworks.core.batterystatsviewer"
-          android:sharedUserId="android.uid.system">
+          package="com.android.frameworks.core.batterystatsviewer">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.BATTERY_STATS"/>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index eb378b9..6a53f68 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -130,23 +130,20 @@
                     || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
                 addEntry(metricTitle, EntryType.UID_POWER_MODELED,
                         requestedBatteryConsumer.getConsumedPower(component),
-                        totalPowerByComponentMah[component]
-                );
+                        totalPowerByComponentMah[component]);
+                addProcessStateEntries(metricTitle, EntryType.UID_POWER_MODELED_PROCESS_STATE,
+                        requestedBatteryConsumer, component);
             } else {
                 addEntry(metricTitle + " (measured)", EntryType.UID_POWER_MEASURED,
                         requestedBatteryConsumer.getConsumedPower(component),
-                        totalPowerByComponentMah[component]
-                );
+                        totalPowerByComponentMah[component]);
                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_MEASURED_PROCESS_STATE,
-                        requestedBatteryConsumer, component
-                );
+                        requestedBatteryConsumer, component);
                 addEntry(metricTitle + " (modeled)", EntryType.UID_POWER_MODELED,
                         requestedModeledBatteryConsumer.getConsumedPower(component),
-                        totalModeledPowerByComponentMah[component]
-                );
+                        totalModeledPowerByComponentMah[component]);
                 addProcessStateEntries(metricTitle, EntryType.UID_POWER_MODELED_PROCESS_STATE,
-                        requestedModeledBatteryConsumer, component
-                );
+                        requestedModeledBatteryConsumer, component);
             }
         }
 
diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp
deleted file mode 100644
index 68416dd..0000000
--- a/core/tests/bluetoothtests/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
-    name: "BluetoothTests",
-    // Include all test java files.
-    srcs: ["src/**/*.java"],
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-    ],
-    static_libs: [
-        "junit",
-        "modules-utils-bytesmatcher",
-    ],
-    platform_apis: true,
-    certificate: "platform",
-}
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
deleted file mode 100644
index 75583d5..0000000
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.bluetooth.tests"
-          android:sharedUserId="android.uid.bluetooth" >
-
-    <uses-permission android:name="android.permission.BLUETOOTH" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
-    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
-    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
-    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
-    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
-    <uses-permission android:name="android.permission.RECEIVE_SMS" />
-    <uses-permission android:name="android.permission.READ_SMS"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-
-    <application >
-        <uses-library android:name="android.test.runner" />
-    </application>
-    <instrumentation android:name="android.bluetooth.BluetoothTestRunner"
-            android:targetPackage="com.android.bluetooth.tests"
-            android:label="Bluetooth Tests" />
-    <instrumentation android:name="android.bluetooth.BluetoothInstrumentation"
-            android:targetPackage="com.android.bluetooth.tests"
-            android:label="Bluetooth Test Utils" />
-
-</manifest>
diff --git a/core/tests/bluetoothtests/AndroidTest.xml b/core/tests/bluetoothtests/AndroidTest.xml
deleted file mode 100644
index f93c4eb..0000000
--- a/core/tests/bluetoothtests/AndroidTest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-<configuration description="Config for Bluetooth test cases">
-    <option name="test-suite-tag" value="apct"/>
-    <option name="test-suite-tag" value="apct-instrumentation"/>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="BluetoothTests.apk" />
-    </target_preparer>
-
-    <option name="test-suite-tag" value="apct"/>
-    <option name="test-tag" value="BluetoothTests"/>
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.bluetooth.tests" />
-        <option name="hidden-api-checks" value="false"/>
-        <option name="runner" value="android.bluetooth.BluetoothTestRunner"/>
-    </test>
-</configuration>
diff --git a/core/tests/bluetoothtests/OWNERS b/core/tests/bluetoothtests/OWNERS
deleted file mode 100644
index 98bb877..0000000
--- a/core/tests/bluetoothtests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /core/java/android/bluetooth/OWNERS
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
deleted file mode 100644
index bd55426..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link BluetoothCodecConfig}.
- * <p>
- * To run this test, use:
- * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
- */
-public class BluetoothCodecConfigTest extends TestCase {
-    private static final int[] kCodecTypeArray = new int[] {
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-        BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
-    };
-    private static final int[] kCodecPriorityArray = new int[] {
-        BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
-        BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-        BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
-    };
-    private static final int[] kSampleRateArray = new int[] {
-        BluetoothCodecConfig.SAMPLE_RATE_NONE,
-        BluetoothCodecConfig.SAMPLE_RATE_44100,
-        BluetoothCodecConfig.SAMPLE_RATE_48000,
-        BluetoothCodecConfig.SAMPLE_RATE_88200,
-        BluetoothCodecConfig.SAMPLE_RATE_96000,
-        BluetoothCodecConfig.SAMPLE_RATE_176400,
-        BluetoothCodecConfig.SAMPLE_RATE_192000,
-    };
-    private static final int[] kBitsPerSampleArray = new int[] {
-        BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-        BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-    };
-    private static final int[] kChannelModeArray = new int[] {
-        BluetoothCodecConfig.CHANNEL_MODE_NONE,
-        BluetoothCodecConfig.CHANNEL_MODE_MONO,
-        BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-    };
-    private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, };
-    private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, };
-    private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, };
-    private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, };
-
-    private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length *
-        kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length *
-        kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length *
-        kCodecSpecific4Array.length;
-
-    private int selectCodecType(int configId) {
-        int left = kCodecTypeArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecTypeArray.length;
-        return kCodecTypeArray[index];
-    }
-
-    private int selectCodecPriority(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecPriorityArray.length;
-        return kCodecPriorityArray[index];
-    }
-
-    private int selectSampleRate(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kSampleRateArray.length;
-        return kSampleRateArray[index];
-    }
-
-    private int selectBitsPerSample(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kBitsPerSampleArray.length;
-        return kBitsPerSampleArray[index];
-    }
-
-    private int selectChannelMode(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kChannelModeArray.length;
-        return kChannelModeArray[index];
-    }
-
-    private long selectCodecSpecific1(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific1Array.length;
-        return kCodecSpecific1Array[index];
-    }
-
-    private long selectCodecSpecific2(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
-            kCodecSpecific2Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific2Array.length;
-        return kCodecSpecific2Array[index];
-    }
-
-    private long selectCodecSpecific3(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
-            kCodecSpecific2Array.length * kCodecSpecific3Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific3Array.length;
-        return kCodecSpecific3Array[index];
-    }
-
-    private long selectCodecSpecific4(int configId) {
-        int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length *
-            kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length *
-            kCodecSpecific2Array.length * kCodecSpecific3Array.length *
-            kCodecSpecific4Array.length;
-        int right = kTotalConfigs / left;
-        int index = configId / right;
-        index = index % kCodecSpecific4Array.length;
-        return kCodecSpecific4Array[index];
-    }
-
-    @SmallTest
-    public void testBluetoothCodecConfig_valid_get_methods() {
-
-        for (int config_id = 0; config_id < kTotalConfigs; config_id++) {
-            int codec_type = selectCodecType(config_id);
-            int codec_priority = selectCodecPriority(config_id);
-            int sample_rate = selectSampleRate(config_id);
-            int bits_per_sample = selectBitsPerSample(config_id);
-            int channel_mode = selectChannelMode(config_id);
-            long codec_specific1 = selectCodecSpecific1(config_id);
-            long codec_specific2 = selectCodecSpecific2(config_id);
-            long codec_specific3 = selectCodecSpecific3(config_id);
-            long codec_specific4 = selectCodecSpecific4(config_id);
-
-            BluetoothCodecConfig bcc = buildBluetoothCodecConfig(codec_type, codec_priority,
-                                                                sample_rate, bits_per_sample,
-                                                                channel_mode, codec_specific1,
-                                                                codec_specific2, codec_specific3,
-                                                                codec_specific4);
-
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
-                assertTrue(bcc.isMandatoryCodec());
-            } else {
-                assertFalse(bcc.isMandatoryCodec());
-            }
-
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) {
-                assertEquals("SBC", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) {
-                assertEquals("AAC", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) {
-                assertEquals("aptX", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) {
-                assertEquals("aptX HD", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) {
-                assertEquals("LDAC", bcc.getCodecName());
-            }
-            if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
-                assertEquals("INVALID CODEC", bcc.getCodecName());
-            }
-
-            assertEquals(codec_type, bcc.getCodecType());
-            assertEquals(codec_priority, bcc.getCodecPriority());
-            assertEquals(sample_rate, bcc.getSampleRate());
-            assertEquals(bits_per_sample, bcc.getBitsPerSample());
-            assertEquals(channel_mode, bcc.getChannelMode());
-            assertEquals(codec_specific1, bcc.getCodecSpecific1());
-            assertEquals(codec_specific2, bcc.getCodecSpecific2());
-            assertEquals(codec_specific3, bcc.getCodecSpecific3());
-            assertEquals(codec_specific4, bcc.getCodecSpecific4());
-        }
-    }
-
-    @SmallTest
-    public void testBluetoothCodecConfig_equals() {
-        BluetoothCodecConfig bcc1 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-
-        BluetoothCodecConfig bcc2_same =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertTrue(bcc1.equals(bcc2_same));
-
-        BluetoothCodecConfig bcc3_codec_type =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc3_codec_type));
-
-        BluetoothCodecConfig bcc4_codec_priority =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc4_codec_priority));
-
-        BluetoothCodecConfig bcc5_sample_rate =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc5_sample_rate));
-
-        BluetoothCodecConfig bcc6_bits_per_sample =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc6_bits_per_sample));
-
-        BluetoothCodecConfig bcc7_channel_mode =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                     1000, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc7_channel_mode));
-
-        BluetoothCodecConfig bcc8_codec_specific1 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1001, 2000, 3000, 4000);
-        assertFalse(bcc1.equals(bcc8_codec_specific1));
-
-        BluetoothCodecConfig bcc9_codec_specific2 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2002, 3000, 4000);
-        assertFalse(bcc1.equals(bcc9_codec_specific2));
-
-        BluetoothCodecConfig bcc10_codec_specific3 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3003, 4000);
-        assertFalse(bcc1.equals(bcc10_codec_specific3));
-
-        BluetoothCodecConfig bcc11_codec_specific4 =
-                buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                     BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                     1000, 2000, 3000, 4004);
-        assertFalse(bcc1.equals(bcc11_codec_specific4));
-    }
-
-    private BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType,
-            int codecPriority, int sampleRate, int bitsPerSample, int channelMode,
-            long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) {
-        return new BluetoothCodecConfig.Builder()
-                    .setCodecType(sourceCodecType)
-                    .setCodecPriority(codecPriority)
-                    .setSampleRate(sampleRate)
-                    .setBitsPerSample(bitsPerSample)
-                    .setChannelMode(channelMode)
-                    .setCodecSpecific1(codecSpecific1)
-                    .setCodecSpecific2(codecSpecific2)
-                    .setCodecSpecific3(codecSpecific3)
-                    .setCodecSpecific4(codecSpecific4)
-                    .build();
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
deleted file mode 100644
index 1cb2dca..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Unit test cases for {@link BluetoothCodecStatus}.
- * <p>
- * To run this test, use:
- * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java
- */
-public class BluetoothCodecStatusTest extends TestCase {
-
-    // Codec configs: A and B are same; C is different
-    private static final BluetoothCodecConfig config_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig config_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig config_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    // Local capabilities: A and B are same; C is different
-    private static final BluetoothCodecConfig local_capability1_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability1_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability1_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-
-    private static final BluetoothCodecConfig local_capability2_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability2_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability2_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability3_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability3_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability3_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability4_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability4_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability4_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability5_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability5_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig local_capability5_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-
-    // Selectable capabilities: A and B are same; C is different
-    private static final BluetoothCodecConfig selectable_capability1_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability1_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability1_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability2_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability2_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability2_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability3_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability3_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability3_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability4_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability4_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability4_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability5_A =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability5_B =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO |
-                                 BluetoothCodecConfig.CHANNEL_MODE_MONO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final BluetoothCodecConfig selectable_capability5_C =
-            buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                                 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
-                                 BluetoothCodecConfig.SAMPLE_RATE_44100 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_48000 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_88200 |
-                                 BluetoothCodecConfig.SAMPLE_RATE_96000,
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_16 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_24 |
-                                 BluetoothCodecConfig.BITS_PER_SAMPLE_32,
-                                 BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                                 1000, 2000, 3000, 4000);
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_A =
-            new ArrayList() {{
-                    add(local_capability1_A);
-                    add(local_capability2_A);
-                    add(local_capability3_A);
-                    add(local_capability4_A);
-                    add(local_capability5_A);
-            }};
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B =
-            new ArrayList() {{
-                    add(local_capability1_B);
-                    add(local_capability2_B);
-                    add(local_capability3_B);
-                    add(local_capability4_B);
-                    add(local_capability5_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B_REORDERED =
-            new ArrayList() {{
-                    add(local_capability5_B);
-                    add(local_capability4_B);
-                    add(local_capability2_B);
-                    add(local_capability3_B);
-                    add(local_capability1_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_C =
-            new ArrayList() {{
-                    add(local_capability1_C);
-                    add(local_capability2_C);
-                    add(local_capability3_C);
-                    add(local_capability4_C);
-                    add(local_capability5_C);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_A =
-            new ArrayList() {{
-                    add(selectable_capability1_A);
-                    add(selectable_capability2_A);
-                    add(selectable_capability3_A);
-                    add(selectable_capability4_A);
-                    add(selectable_capability5_A);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B =
-            new ArrayList() {{
-                    add(selectable_capability1_B);
-                    add(selectable_capability2_B);
-                    add(selectable_capability3_B);
-                    add(selectable_capability4_B);
-                    add(selectable_capability5_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B_REORDERED =
-            new ArrayList() {{
-                    add(selectable_capability5_B);
-                    add(selectable_capability4_B);
-                    add(selectable_capability2_B);
-                    add(selectable_capability3_B);
-                    add(selectable_capability1_B);
-            }};
-
-    private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_C =
-            new ArrayList() {{
-                    add(selectable_capability1_C);
-                    add(selectable_capability2_C);
-                    add(selectable_capability3_C);
-                    add(selectable_capability4_C);
-                    add(selectable_capability5_C);
-            }};
-
-    private static final BluetoothCodecStatus bcs_A =
-            new BluetoothCodecStatus(config_A, LOCAL_CAPABILITY_A, SELECTABLE_CAPABILITY_A);
-    private static final BluetoothCodecStatus bcs_B =
-            new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B, SELECTABLE_CAPABILITY_B);
-    private static final BluetoothCodecStatus bcs_B_reordered =
-            new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B_REORDERED,
-                                 SELECTABLE_CAPABILITY_B_REORDERED);
-    private static final BluetoothCodecStatus bcs_C =
-            new BluetoothCodecStatus(config_C, LOCAL_CAPABILITY_C, SELECTABLE_CAPABILITY_C);
-
-    @SmallTest
-    public void testBluetoothCodecStatus_get_methods() {
-
-        assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A));
-        assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B));
-        assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C));
-
-        assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_A));
-        assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_B));
-        assertFalse(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_C));
-
-        assertTrue(bcs_A.getCodecsSelectableCapabilities()
-                                 .equals(SELECTABLE_CAPABILITY_A));
-        assertTrue(bcs_A.getCodecsSelectableCapabilities()
-                                  .equals(SELECTABLE_CAPABILITY_B));
-        assertFalse(bcs_A.getCodecsSelectableCapabilities()
-                                  .equals(SELECTABLE_CAPABILITY_C));
-    }
-
-    @SmallTest
-    public void testBluetoothCodecStatus_equals() {
-        assertTrue(bcs_A.equals(bcs_B));
-        assertTrue(bcs_B.equals(bcs_A));
-        assertTrue(bcs_A.equals(bcs_B_reordered));
-        assertTrue(bcs_B_reordered.equals(bcs_A));
-        assertFalse(bcs_A.equals(bcs_C));
-        assertFalse(bcs_C.equals(bcs_A));
-    }
-
-    private static BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType,
-            int codecPriority, int sampleRate, int bitsPerSample, int channelMode,
-            long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) {
-        return new BluetoothCodecConfig.Builder()
-                    .setCodecType(sourceCodecType)
-                    .setCodecPriority(codecPriority)
-                    .setSampleRate(sampleRate)
-                    .setBitsPerSample(bitsPerSample)
-                    .setChannelMode(channelMode)
-                    .setCodecSpecific1(codecSpecific1)
-                    .setCodecSpecific2(codecSpecific2)
-                    .setCodecSpecific3(codecSpecific3)
-                    .setCodecSpecific4(codecSpecific4)
-                    .build();
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
deleted file mode 100644
index 37b2a50..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.os.Bundle;
-
-import junit.framework.Assert;
-
-import java.util.Set;
-
-public class BluetoothInstrumentation extends Instrumentation {
-
-    private BluetoothTestUtils mUtils = null;
-    private BluetoothAdapter mAdapter = null;
-    private Bundle mArgs = null;
-    private Bundle mSuccessResult = null;
-
-    private BluetoothTestUtils getBluetoothTestUtils() {
-        if (mUtils == null) {
-            mUtils = new BluetoothTestUtils(getContext(),
-                    BluetoothInstrumentation.class.getSimpleName());
-        }
-        return mUtils;
-    }
-
-    private BluetoothAdapter getBluetoothAdapter() {
-        if (mAdapter == null) {
-            mAdapter = ((BluetoothManager)getContext().getSystemService(
-                    Context.BLUETOOTH_SERVICE)).getAdapter();
-        }
-        return mAdapter;
-    }
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        super.onCreate(arguments);
-        mArgs = arguments;
-        // create the default result response, but only use it in success code path
-        mSuccessResult = new Bundle();
-        mSuccessResult.putString("result", "SUCCESS");
-        start();
-    }
-
-    @Override
-    public void onStart() {
-        String command = mArgs.getString("command");
-        if ("enable".equals(command)) {
-            enable();
-        } else if ("disable".equals(command)) {
-            disable();
-        } else if ("unpairAll".equals(command)) {
-            unpairAll();
-        } else if ("getName".equals(command)) {
-            getName();
-        } else if ("getAddress".equals(command)) {
-            getAddress();
-        } else if ("getBondedDevices".equals(command)) {
-            getBondedDevices();
-        } else {
-            finish(null);
-        }
-    }
-
-    public void enable() {
-        getBluetoothTestUtils().enable(getBluetoothAdapter());
-        finish(mSuccessResult);
-    }
-
-    public void disable() {
-        getBluetoothTestUtils().disable(getBluetoothAdapter());
-        finish(mSuccessResult);
-    }
-
-    public void unpairAll() {
-        getBluetoothTestUtils().unpairAll(getBluetoothAdapter());
-        finish(mSuccessResult);
-    }
-
-    public void getName() {
-        String name = getBluetoothAdapter().getName();
-        mSuccessResult.putString("name", name);
-        finish(mSuccessResult);
-    }
-
-    public void getAddress() {
-        String name = getBluetoothAdapter().getAddress();
-        mSuccessResult.putString("address", name);
-        finish(mSuccessResult);
-    }
-
-    public void getBondedDevices() {
-        Set<BluetoothDevice> devices = getBluetoothAdapter().getBondedDevices();
-        int i = 0;
-        for (BluetoothDevice device : devices) {
-            mSuccessResult.putString(String.format("device-%02d", i), device.getAddress());
-            i++;
-        }
-        finish(mSuccessResult);
-    }
-
-    public void finish(Bundle result) {
-        if (result == null) {
-            result = new Bundle();
-        }
-        finish(Activity.RESULT_OK, result);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java
deleted file mode 100644
index c3d707c..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link BluetoothLeAudioCodecConfig}.
- */
-public class BluetoothLeAudioCodecConfigTest extends TestCase {
-    private int[] mCodecTypeArray = new int[] {
-        BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3,
-        BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID,
-    };
-
-    @SmallTest
-    public void testBluetoothLeAudioCodecConfig_valid_get_methods() {
-
-        for (int codecIdx = 0; codecIdx < mCodecTypeArray.length; codecIdx++) {
-            int codecType = mCodecTypeArray[codecIdx];
-
-            BluetoothLeAudioCodecConfig leAudioCodecConfig =
-                    buildBluetoothLeAudioCodecConfig(codecType);
-
-            if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) {
-                assertEquals("LC3", leAudioCodecConfig.getCodecName());
-            }
-            if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
-                assertEquals("INVALID CODEC", leAudioCodecConfig.getCodecName());
-            }
-
-            assertEquals(codecType, leAudioCodecConfig.getCodecType());
-        }
-    }
-
-    private BluetoothLeAudioCodecConfig buildBluetoothLeAudioCodecConfig(int sourceCodecType) {
-        return new BluetoothLeAudioCodecConfig.Builder()
-                    .setCodecType(sourceCodecType)
-                    .build();
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
deleted file mode 100644
index 33e9dd7..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2010 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.bluetooth;
-
-import android.content.Context;
-import android.test.InstrumentationTestCase;
-
-/**
- * Instrumentation test case for stress test involving rebooting the device.
- * <p>
- * This test case tests that bluetooth is enabled after a device reboot. Because
- * the device will reboot, the instrumentation must be driven by a script on the
- * host side.
- */
-public class BluetoothRebootStressTest extends InstrumentationTestCase {
-    private static final String TAG = "BluetoothRebootStressTest";
-    private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt";
-
-    private BluetoothTestUtils mTestUtils;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Context context = getInstrumentation().getTargetContext();
-        mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        mTestUtils.close();
-    }
-
-    /**
-     * Test method used to start the test by turning bluetooth on.
-     */
-    public void testStart() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        mTestUtils.enable(adapter);
-    }
-
-    /**
-     * Test method used in the middle iterations of the test to check if
-     * bluetooth is on. Does not toggle bluetooth after the check. Assumes that
-     * bluetooth has been turned on by {@code #testStart()}
-     */
-    public void testMiddleNoToggle() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        assertTrue(adapter.isEnabled());
-    }
-
-    /**
-     * Test method used in the middle iterations of the test to check if
-     * bluetooth is on. Toggles bluetooth after the check. Assumes that
-     * bluetooth has been turned on by {@code #testStart()}
-     */
-    public void testMiddleToggle() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        assertTrue(adapter.isEnabled());
-
-        mTestUtils.disable(adapter);
-        mTestUtils.enable(adapter);
-    }
-
-    /**
-     * Test method used in the stop the test by turning bluetooth off. Assumes
-     * that bluetooth has been turned on by {@code #testStart()}
-     */
-    public void testStop() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        assertTrue(adapter.isEnabled());
-
-        mTestUtils.disable(adapter);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
deleted file mode 100644
index 89dbe3f..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2010 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.bluetooth;
-
-import android.content.Context;
-import android.test.InstrumentationTestCase;
-
-/**
- * Stress test suite for Bluetooth related functions.
- *
- * Includes tests for enabling/disabling bluetooth, enabling/disabling discoverable mode,
- * starting/stopping scans, connecting/disconnecting to HFP, A2DP, HID, PAN profiles, and verifying
- * that remote connections/disconnections occur for the PAN profile.
- * <p>
- * This test suite uses {@link android.bluetooth.BluetoothTestRunner} to for parameters such as the
- * number of iterations and the addresses of remote Bluetooth devices.
- */
-public class BluetoothStressTest extends InstrumentationTestCase {
-    private static final String TAG = "BluetoothStressTest";
-    private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt";
-    /** The amount of time to sleep between issuing start/stop SCO in ms. */
-    private static final long SCO_SLEEP_TIME = 2 * 1000;
-
-    private BluetoothAdapter mAdapter;
-    private BluetoothTestUtils mTestUtils;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Context context = getInstrumentation().getTargetContext();
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
-
-        // Start all tests in a disabled state.
-        if (mAdapter.isEnabled()) {
-            mTestUtils.disable(mAdapter);
-        }
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-        mTestUtils.close();
-    }
-
-    /**
-     * Stress test for enabling and disabling Bluetooth.
-     */
-    public void testEnable() {
-        int iterations = BluetoothTestRunner.sEnableIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.enable(mAdapter);
-            mTestUtils.disable(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for putting the device in and taking the device out of discoverable mode.
-     */
-    public void testDiscoverable() {
-        int iterations = BluetoothTestRunner.sDiscoverableIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        mTestUtils.enable(mAdapter);
-        mTestUtils.undiscoverable(mAdapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.discoverable(mAdapter);
-            mTestUtils.undiscoverable(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for starting and stopping Bluetooth scans.
-     */
-    public void testScan() {
-        int iterations = BluetoothTestRunner.sScanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        mTestUtils.enable(mAdapter);
-        mTestUtils.stopScan(mAdapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.startScan(mAdapter);
-            mTestUtils.stopScan(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for enabling and disabling the PAN NAP profile.
-     */
-    public void testEnablePan() {
-        int iterations = BluetoothTestRunner.sEnablePanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        mTestUtils.enable(mAdapter);
-        mTestUtils.disablePan(mAdapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of "
-                    + iterations);
-            mTestUtils.enablePan(mAdapter);
-            mTestUtils.disablePan(mAdapter);
-        }
-    }
-
-    /**
-     * Stress test for pairing and unpairing with a remote device.
-     * <p>
-     * In this test, the local device initiates pairing with a remote device, and then unpairs with
-     * the device after the pairing has successfully completed.
-     */
-    public void testPair() {
-        int iterations = BluetoothTestRunner.sPairIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                    BluetoothTestRunner.sDevicePairPin);
-            mTestUtils.unpair(mAdapter, device);
-        }
-    }
-
-    /**
-     * Stress test for accepting a pairing request and unpairing with a remote device.
-     * <p>
-     * In this test, the local device waits for a pairing request from a remote device.  It accepts
-     * the request and then unpairs after the paring has successfully completed.
-     */
-    public void testAcceptPair() {
-        int iterations = BluetoothTestRunner.sPairIterations;
-        if (iterations == 0) {
-            return;
-        }
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                    BluetoothTestRunner.sDevicePairPin);
-            mTestUtils.unpair(mAdapter, device);
-        }
-    }
-
-    /**
-     * Stress test for connecting and disconnecting with an A2DP source.
-     * <p>
-     * In this test, the local device plays the role of an A2DP sink, and initiates connections and
-     * disconnections with an A2DP source.
-     */
-    public void testConnectA2dp() {
-        int iterations = BluetoothTestRunner.sConnectA2dpIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, null);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.A2DP,
-                    String.format("connectA2dp(device=%s)", device));
-            mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP,
-                    String.format("disconnectA2dp(device=%s)", device));
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for connecting and disconnecting the HFP with a hands free device.
-     * <p>
-     * In this test, the local device plays the role of an HFP audio gateway, and initiates
-     * connections and disconnections with a hands free device.
-     */
-    public void testConnectHeadset() {
-        int iterations = BluetoothTestRunner.sConnectHeadsetIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET,
-                    String.format("connectHeadset(device=%s)", device));
-            mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET,
-                    String.format("disconnectHeadset(device=%s)", device));
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for connecting and disconnecting with a HID device.
-     * <p>
-     * In this test, the local device plays the role of a HID host, and initiates connections and
-     * disconnections with a HID device.
-     */
-    public void testConnectInput() {
-        int iterations = BluetoothTestRunner.sConnectInputIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST,
-                    String.format("connectInput(device=%s)", device));
-            mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST,
-                    String.format("disconnectInput(device=%s)", device));
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for connecting and disconnecting with a PAN NAP.
-     * <p>
-     * In this test, the local device plays the role of a PANU, and initiates connections and
-     * disconnections with a NAP.
-     */
-    public void testConnectPan() {
-        int iterations = BluetoothTestRunner.sConnectPanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectPan(mAdapter, device);
-            mTestUtils.disconnectPan(mAdapter, device);
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /**
-     * Stress test for verifying a PANU connecting and disconnecting with the device.
-     * <p>
-     * In this test, the local device plays the role of a NAP which a remote PANU connects and
-     * disconnects from.
-     */
-    public void testIncomingPanConnection() {
-        int iterations = BluetoothTestRunner.sConnectPanIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.disablePan(mAdapter);
-        mTestUtils.enablePan(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of "
-                    + iterations);
-            mTestUtils.incomingPanConnection(mAdapter, device);
-            mTestUtils.incomingPanDisconnection(mAdapter, device);
-        }
-
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.disablePan(mAdapter);
-    }
-
-    /**
-     * Stress test for verifying that AudioManager can open and close SCO connections.
-     * <p>
-     * In this test, a HSP connection is opened with an external headset and the SCO connection is
-     * repeatibly opened and closed.
-     */
-    public void testStartStopSco() {
-        int iterations = BluetoothTestRunner.sStartStopScoIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.unpair(mAdapter, device);
-        mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
-                BluetoothTestRunner.sDevicePairPin);
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-        mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-        mTestUtils.stopSco(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.startSco(mAdapter, device);
-            sleep(SCO_SLEEP_TIME);
-            mTestUtils.stopSco(mAdapter, device);
-            sleep(SCO_SLEEP_TIME);
-        }
-
-        mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null);
-        mTestUtils.unpair(mAdapter, device);
-    }
-
-    /* Make sure there is at least 1 unread message in the last week on remote device */
-    public void testMceSetMessageStatus() {
-        int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations;
-        if (iterations == 0) {
-            return;
-        }
-
-        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
-        mTestUtils.enable(mAdapter);
-        mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null);
-        mTestUtils.mceGetUnreadMessage(mAdapter, device);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ);
-            mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD);
-        }
-
-        /**
-         * It is hard to find device to support set undeleted status, so just
-         * set deleted in 1 iteration
-         **/
-        mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED);
-    }
-
-    private void sleep(long time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-        }
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
deleted file mode 100644
index d19c2c3..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2010 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.bluetooth;
-
-import junit.framework.TestSuite;
-
-import android.os.Bundle;
-import android.test.InstrumentationTestRunner;
-import android.test.InstrumentationTestSuite;
-import android.util.Log;
-
-/**
- * Instrumentation test runner for Bluetooth tests.
- * <p>
- * To run:
- * <pre>
- * {@code
- * adb shell am instrument \
- *     [-e enable_iterations <iterations>] \
- *     [-e discoverable_iterations <iterations>] \
- *     [-e scan_iterations <iterations>] \
- *     [-e enable_pan_iterations <iterations>] \
- *     [-e pair_iterations <iterations>] \
- *     [-e connect_a2dp_iterations <iterations>] \
- *     [-e connect_headset_iterations <iterations>] \
- *     [-e connect_input_iterations <iterations>] \
- *     [-e connect_pan_iterations <iterations>] \
- *     [-e start_stop_sco_iterations <iterations>] \
- *     [-e mce_set_message_status_iterations <iterations>] \
- *     [-e pair_address <address>] \
- *     [-e headset_address <address>] \
- *     [-e a2dp_address <address>] \
- *     [-e input_address <address>] \
- *     [-e pan_address <address>] \
- *     [-e pair_pin <pin>] \
- *     [-e pair_passkey <passkey>] \
- *     -w com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner
- * }
- * </pre>
- */
-public class BluetoothTestRunner extends InstrumentationTestRunner {
-    private static final String TAG = "BluetoothTestRunner";
-
-    public static int sEnableIterations = 100;
-    public static int sDiscoverableIterations = 1000;
-    public static int sScanIterations = 1000;
-    public static int sEnablePanIterations = 1000;
-    public static int sPairIterations = 100;
-    public static int sConnectHeadsetIterations = 100;
-    public static int sConnectA2dpIterations = 100;
-    public static int sConnectInputIterations = 100;
-    public static int sConnectPanIterations = 100;
-    public static int sStartStopScoIterations = 100;
-    public static int sMceSetMessageStatusIterations = 100;
-
-    public static String sDeviceAddress = "";
-    public static byte[] sDevicePairPin = {'1', '2', '3', '4'};
-    public static int sDevicePairPasskey = 123456;
-
-    @Override
-    public TestSuite getAllTests() {
-        TestSuite suite = new InstrumentationTestSuite(this);
-        suite.addTestSuite(BluetoothStressTest.class);
-        return suite;
-    }
-
-    @Override
-    public ClassLoader getLoader() {
-        return BluetoothTestRunner.class.getClassLoader();
-    }
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        String val = arguments.getString("enable_iterations");
-        if (val != null) {
-            try {
-                sEnableIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("discoverable_iterations");
-        if (val != null) {
-            try {
-                sDiscoverableIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("scan_iterations");
-        if (val != null) {
-            try {
-                sScanIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("enable_pan_iterations");
-        if (val != null) {
-            try {
-                sEnablePanIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("pair_iterations");
-        if (val != null) {
-            try {
-                sPairIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_a2dp_iterations");
-        if (val != null) {
-            try {
-                sConnectA2dpIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_headset_iterations");
-        if (val != null) {
-            try {
-                sConnectHeadsetIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_input_iterations");
-        if (val != null) {
-            try {
-                sConnectInputIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("connect_pan_iterations");
-        if (val != null) {
-            try {
-                sConnectPanIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("start_stop_sco_iterations");
-        if (val != null) {
-            try {
-                sStartStopScoIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("mce_set_message_status_iterations");
-        if (val != null) {
-            try {
-                sMceSetMessageStatusIterations = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        val = arguments.getString("device_address");
-        if (val != null) {
-            sDeviceAddress = val;
-        }
-
-        val = arguments.getString("device_pair_pin");
-        if (val != null) {
-            byte[] pin = BluetoothDevice.convertPinToBytes(val);
-            if (pin != null) {
-                sDevicePairPin = pin;
-            }
-        }
-
-        val = arguments.getString("device_pair_passkey");
-        if (val != null) {
-            try {
-                sDevicePairPasskey = Integer.parseInt(val);
-            } catch (NumberFormatException e) {
-                // Invalid argument, fall back to default value
-            }
-        }
-
-        Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations));
-        Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations));
-        Log.i(TAG, String.format("scan_iterations=%d", sScanIterations));
-        Log.i(TAG, String.format("pair_iterations=%d", sPairIterations));
-        Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations));
-        Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations));
-        Log.i(TAG, String.format("connect_input_iterations=%d", sConnectInputIterations));
-        Log.i(TAG, String.format("connect_pan_iterations=%d", sConnectPanIterations));
-        Log.i(TAG, String.format("start_stop_sco_iterations=%d", sStartStopScoIterations));
-        Log.i(TAG, String.format("device_address=%s", sDeviceAddress));
-        Log.i(TAG, String.format("device_pair_pin=%s", new String(sDevicePairPin)));
-        Log.i(TAG, String.format("device_pair_passkey=%d", sDevicePairPasskey));
-
-        // Call onCreate last since we want to set the static variables first.
-        super.onCreate(arguments);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
deleted file mode 100644
index 8eb6ebc..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ /dev/null
@@ -1,1651 +0,0 @@
-/*
- * Copyright (C) 2010 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.bluetooth;
-
-import android.bluetooth.BluetoothPan;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.os.Environment;
-import android.util.Log;
-
-import junit.framework.Assert;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-public class BluetoothTestUtils extends Assert {
-
-    /** Timeout for enable/disable in ms. */
-    private static final int ENABLE_DISABLE_TIMEOUT = 20000;
-    /** Timeout for discoverable/undiscoverable in ms. */
-    private static final int DISCOVERABLE_UNDISCOVERABLE_TIMEOUT = 5000;
-    /** Timeout for starting/stopping a scan in ms. */
-    private static final int START_STOP_SCAN_TIMEOUT = 5000;
-    /** Timeout for pair/unpair in ms. */
-    private static final int PAIR_UNPAIR_TIMEOUT = 20000;
-    /** Timeout for connecting/disconnecting a profile in ms. */
-    private static final int CONNECT_DISCONNECT_PROFILE_TIMEOUT = 20000;
-    /** Timeout to start or stop a SCO channel in ms. */
-    private static final int START_STOP_SCO_TIMEOUT = 10000;
-    /** Timeout to connect a profile proxy in ms. */
-    private static final int CONNECT_PROXY_TIMEOUT = 5000;
-    /** Time between polls in ms. */
-    private static final int POLL_TIME = 100;
-    /** Timeout to get map message in ms. */
-    private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000;
-    /** Timeout to set map message status in ms. */
-    private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000;
-
-    private abstract class FlagReceiver extends BroadcastReceiver {
-        private int mExpectedFlags = 0;
-        private int mFiredFlags = 0;
-        private long mCompletedTime = -1;
-
-        public FlagReceiver(int expectedFlags) {
-            mExpectedFlags = expectedFlags;
-        }
-
-        public int getFiredFlags() {
-            synchronized (this) {
-                return mFiredFlags;
-            }
-        }
-
-        public long getCompletedTime() {
-            synchronized (this) {
-                return mCompletedTime;
-            }
-        }
-
-        protected void setFiredFlag(int flag) {
-            synchronized (this) {
-                mFiredFlags |= flag;
-                if ((mFiredFlags & mExpectedFlags) == mExpectedFlags) {
-                    mCompletedTime = System.currentTimeMillis();
-                }
-            }
-        }
-    }
-
-    private class BluetoothReceiver extends FlagReceiver {
-        private static final int DISCOVERY_STARTED_FLAG = 1;
-        private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
-        private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
-        private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
-        private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
-        private static final int STATE_OFF_FLAG = 1 << 5;
-        private static final int STATE_TURNING_ON_FLAG = 1 << 6;
-        private static final int STATE_ON_FLAG = 1 << 7;
-        private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
-        private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9;
-        private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10;
-
-        public BluetoothReceiver(int expectedFlags) {
-            super(expectedFlags);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
-                setFiredFlag(DISCOVERY_STARTED_FLAG);
-            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
-                setFiredFlag(DISCOVERY_FINISHED_FLAG);
-            } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
-                int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1);
-                assertNotSame(-1, mode);
-                switch (mode) {
-                    case BluetoothAdapter.SCAN_MODE_NONE:
-                        setFiredFlag(SCAN_MODE_NONE_FLAG);
-                        break;
-                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
-                        setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG);
-                        break;
-                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
-                        setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG);
-                        break;
-                }
-            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
-                assertNotSame(-1, state);
-                switch (state) {
-                    case BluetoothAdapter.STATE_OFF:
-                        setFiredFlag(STATE_OFF_FLAG);
-                        break;
-                    case BluetoothAdapter.STATE_TURNING_ON:
-                        setFiredFlag(STATE_TURNING_ON_FLAG);
-                        break;
-                    case BluetoothAdapter.STATE_ON:
-                        setFiredFlag(STATE_ON_FLAG);
-                        break;
-                    case BluetoothAdapter.STATE_TURNING_OFF:
-                        setFiredFlag(STATE_TURNING_OFF_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-    private class PairReceiver extends FlagReceiver {
-        private static final int STATE_BONDED_FLAG = 1;
-        private static final int STATE_BONDING_FLAG = 1 << 1;
-        private static final int STATE_NONE_FLAG = 1 << 2;
-
-        private BluetoothDevice mDevice;
-        private int mPasskey;
-        private byte[] mPin;
-
-        public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) {
-            super(expectedFlags);
-
-            mDevice = device;
-            mPasskey = passkey;
-            mPin = pin;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
-                return;
-            }
-
-            if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
-                int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1);
-                assertNotSame(-1, varient);
-                switch (varient) {
-                    case BluetoothDevice.PAIRING_VARIANT_PIN:
-                    case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS:
-                        mDevice.setPin(mPin);
-                        break;
-                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                        break;
-                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
-                    case BluetoothDevice.PAIRING_VARIANT_CONSENT:
-                        mDevice.setPairingConfirmation(true);
-                        break;
-                    case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                        break;
-                }
-            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
-                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
-                assertNotSame(-1, state);
-                switch (state) {
-                    case BluetoothDevice.BOND_NONE:
-                        setFiredFlag(STATE_NONE_FLAG);
-                        break;
-                    case BluetoothDevice.BOND_BONDING:
-                        setFiredFlag(STATE_BONDING_FLAG);
-                        break;
-                    case BluetoothDevice.BOND_BONDED:
-                        setFiredFlag(STATE_BONDED_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-    private class ConnectProfileReceiver extends FlagReceiver {
-        private static final int STATE_DISCONNECTED_FLAG = 1;
-        private static final int STATE_CONNECTING_FLAG = 1 << 1;
-        private static final int STATE_CONNECTED_FLAG = 1 << 2;
-        private static final int STATE_DISCONNECTING_FLAG = 1 << 3;
-
-        private BluetoothDevice mDevice;
-        private int mProfile;
-        private String mConnectionAction;
-
-        public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) {
-            super(expectedFlags);
-
-            mDevice = device;
-            mProfile = profile;
-
-            switch (mProfile) {
-                case BluetoothProfile.A2DP:
-                    mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.HEADSET:
-                    mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.HID_HOST:
-                    mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.PAN:
-                    mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                case BluetoothProfile.MAP_CLIENT:
-                    mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED;
-                    break;
-                default:
-                    mConnectionAction = null;
-            }
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) {
-                if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
-                    return;
-                }
-
-                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-                assertNotSame(-1, state);
-                switch (state) {
-                    case BluetoothProfile.STATE_DISCONNECTED:
-                        setFiredFlag(STATE_DISCONNECTED_FLAG);
-                        break;
-                    case BluetoothProfile.STATE_CONNECTING:
-                        setFiredFlag(STATE_CONNECTING_FLAG);
-                        break;
-                    case BluetoothProfile.STATE_CONNECTED:
-                        setFiredFlag(STATE_CONNECTED_FLAG);
-                        break;
-                    case BluetoothProfile.STATE_DISCONNECTING:
-                        setFiredFlag(STATE_DISCONNECTING_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-    private class ConnectPanReceiver extends ConnectProfileReceiver {
-        private int mRole;
-
-        public ConnectPanReceiver(BluetoothDevice device, int role, int expectedFlags) {
-            super(device, BluetoothProfile.PAN, expectedFlags);
-
-            mRole = role;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mRole != intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, -1)) {
-                return;
-            }
-
-            super.onReceive(context, intent);
-        }
-    }
-
-    private class StartStopScoReceiver extends FlagReceiver {
-        private static final int STATE_CONNECTED_FLAG = 1;
-        private static final int STATE_DISCONNECTED_FLAG = 1 << 1;
-
-        public StartStopScoReceiver(int expectedFlags) {
-            super(expectedFlags);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())) {
-                int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
-                        AudioManager.SCO_AUDIO_STATE_ERROR);
-                assertNotSame(AudioManager.SCO_AUDIO_STATE_ERROR, state);
-                switch(state) {
-                    case AudioManager.SCO_AUDIO_STATE_CONNECTED:
-                        setFiredFlag(STATE_CONNECTED_FLAG);
-                        break;
-                    case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
-                        setFiredFlag(STATE_DISCONNECTED_FLAG);
-                        break;
-                }
-            }
-        }
-    }
-
-
-    private class MceSetMessageStatusReceiver extends FlagReceiver {
-        private static final int MESSAGE_RECEIVED_FLAG = 1;
-        private static final int STATUS_CHANGED_FLAG = 1 << 1;
-
-        public MceSetMessageStatusReceiver(int expectedFlags) {
-            super(expectedFlags);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
-                String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE);
-                assertNotNull(handle);
-                setFiredFlag(MESSAGE_RECEIVED_FLAG);
-                mMsgHandle = handle;
-            } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) {
-                int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
-                assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
-                setFiredFlag(STATUS_CHANGED_FLAG);
-            } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) {
-                int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
-                assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
-                setFiredFlag(STATUS_CHANGED_FLAG);
-            }
-        }
-    }
-
-    private BluetoothProfile.ServiceListener mServiceListener =
-            new BluetoothProfile.ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            synchronized (this) {
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = (BluetoothA2dp) proxy;
-                        break;
-                    case BluetoothProfile.HEADSET:
-                        mHeadset = (BluetoothHeadset) proxy;
-                        break;
-                    case BluetoothProfile.HID_HOST:
-                        mInput = (BluetoothHidHost) proxy;
-                        break;
-                    case BluetoothProfile.PAN:
-                        mPan = (BluetoothPan) proxy;
-                        break;
-                    case BluetoothProfile.MAP_CLIENT:
-                        mMce = (BluetoothMapClient) proxy;
-                        break;
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(int profile) {
-            synchronized (this) {
-                switch (profile) {
-                    case BluetoothProfile.A2DP:
-                        mA2dp = null;
-                        break;
-                    case BluetoothProfile.HEADSET:
-                        mHeadset = null;
-                        break;
-                    case BluetoothProfile.HID_HOST:
-                        mInput = null;
-                        break;
-                    case BluetoothProfile.PAN:
-                        mPan = null;
-                        break;
-                    case BluetoothProfile.MAP_CLIENT:
-                        mMce = null;
-                        break;
-                }
-            }
-        }
-    };
-
-    private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>();
-
-    private BufferedWriter mOutputWriter;
-    private String mTag;
-    private String mOutputFile;
-
-    private Context mContext;
-    private BluetoothA2dp mA2dp = null;
-    private BluetoothHeadset mHeadset = null;
-    private BluetoothHidHost mInput = null;
-    private BluetoothPan mPan = null;
-    private BluetoothMapClient mMce = null;
-    private String mMsgHandle = null;
-
-    /**
-     * Creates a utility instance for testing Bluetooth.
-     *
-     * @param context The context of the application using the utility.
-     * @param tag The log tag of the application using the utility.
-     */
-    public BluetoothTestUtils(Context context, String tag) {
-        this(context, tag, null);
-    }
-
-    /**
-     * Creates a utility instance for testing Bluetooth.
-     *
-     * @param context The context of the application using the utility.
-     * @param tag The log tag of the application using the utility.
-     * @param outputFile The path to an output file if the utility is to write results to a
-     *        separate file.
-     */
-    public BluetoothTestUtils(Context context, String tag, String outputFile) {
-        mContext = context;
-        mTag = tag;
-        mOutputFile = outputFile;
-
-        if (mOutputFile == null) {
-            mOutputWriter = null;
-        } else {
-            try {
-                mOutputWriter = new BufferedWriter(new FileWriter(new File(
-                        Environment.getExternalStorageDirectory(), mOutputFile), true));
-            } catch (IOException e) {
-                Log.w(mTag, "Test output file could not be opened", e);
-                mOutputWriter = null;
-            }
-        }
-    }
-
-    /**
-     * Closes the utility instance and unregisters any BroadcastReceivers.
-     */
-    public void close() {
-        while (!mReceivers.isEmpty()) {
-            mContext.unregisterReceiver(mReceivers.remove(0));
-        }
-
-        if (mOutputWriter != null) {
-            try {
-                mOutputWriter.close();
-            } catch (IOException e) {
-                Log.w(mTag, "Test output file could not be closed", e);
-            }
-        }
-    }
-
-    /**
-     * Enables Bluetooth and checks to make sure that Bluetooth was turned on and that the correct
-     * actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void enable(BluetoothAdapter adapter) {
-        writeOutput("Enabling Bluetooth adapter.");
-        assertFalse(adapter.isEnabled());
-        int btState = adapter.getState();
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                        BluetoothAdapter.ERROR);
-                if (state == BluetoothAdapter.STATE_ON) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
-        // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
-        // So no assertion applied here.
-        adapter.enable();
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
-            writeOutput(String.format("enable() completed in 0 ms"));
-        } catch (final InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("enable() timeout: state=%d (expected %d)", btState,
-                    BluetoothAdapter.STATE_ON));
-        }
-    }
-
-    /**
-     * Disables Bluetooth and checks to make sure that Bluetooth was turned off and that the correct
-     * actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void disable(BluetoothAdapter adapter) {
-        writeOutput("Disabling Bluetooth adapter.");
-        assertTrue(adapter.isEnabled());
-        int btState = adapter.getState();
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                        BluetoothAdapter.ERROR);
-                if (state == BluetoothAdapter.STATE_OFF) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
-        // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
-        // So no assertion applied here.
-        adapter.disable();
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
-            writeOutput(String.format("disable() completed in 0 ms"));
-        } catch (final InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("disable() timeout: state=%d (expected %d)", btState,
-                    BluetoothAdapter.STATE_OFF));
-        }
-    }
-
-    /**
-     * Puts the local device into discoverable mode and checks to make sure that the local device
-     * is in discoverable mode and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void discoverable(BluetoothAdapter adapter) {
-        if (!adapter.isEnabled()) {
-            fail("discoverable() bluetooth not enabled");
-        }
-
-        int scanMode = adapter.getScanMode();
-        if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-            return;
-        }
-
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                        BluetoothAdapter.SCAN_MODE_NONE);
-                if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE),
-                BluetoothStatusCodes.SUCCESS);
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
-                    TimeUnit.MILLISECONDS);
-            writeOutput(String.format("discoverable() completed in 0 ms"));
-        } catch (final InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("discoverable() timeout: scanMode=%d (expected %d)", scanMode,
-                    BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
-        }
-    }
-
-    /**
-     * Puts the local device into connectable only mode and checks to make sure that the local
-     * device is in in connectable mode and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void undiscoverable(BluetoothAdapter adapter) {
-        if (!adapter.isEnabled()) {
-            fail("undiscoverable() bluetooth not enabled");
-        }
-
-        int scanMode = adapter.getScanMode();
-        if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-            return;
-        }
-
-        final Semaphore completionSemaphore = new Semaphore(0);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
-                    return;
-                }
-                final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                        BluetoothAdapter.SCAN_MODE_NONE);
-                if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-                    completionSemaphore.release();
-                }
-            }
-        };
-
-        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-        mContext.registerReceiver(receiver, filter);
-        assertEquals(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE),
-                BluetoothStatusCodes.SUCCESS);
-        boolean success = false;
-        try {
-            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
-                    TimeUnit.MILLISECONDS);
-            writeOutput(String.format("undiscoverable() completed in 0 ms"));
-        } catch (InterruptedException e) {
-            // This should never happen but just in case it does, the test will fail anyway.
-        }
-        mContext.unregisterReceiver(receiver);
-        if (!success) {
-            fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d)", scanMode,
-                    BluetoothAdapter.SCAN_MODE_CONNECTABLE));
-        }
-    }
-
-    /**
-     * Starts a scan for remote devices and checks to make sure that the local device is scanning
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void startScan(BluetoothAdapter adapter) {
-        int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG;
-
-        if (!adapter.isEnabled()) {
-            fail("startScan() bluetooth not enabled");
-        }
-
-        if (adapter.isDiscovering()) {
-            return;
-        }
-
-        BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
-        long start = System.currentTimeMillis();
-        assertTrue(adapter.startDiscovery());
-
-        while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) {
-            if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
-                writeOutput(String.format("startScan() completed in %d ms",
-                        (receiver.getCompletedTime() - start)));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
-                adapter.isDiscovering(), firedFlags, mask));
-    }
-
-    /**
-     * Stops a scan for remote devices and checks to make sure that the local device is not scanning
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void stopScan(BluetoothAdapter adapter) {
-        int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG;
-
-        if (!adapter.isEnabled()) {
-            fail("stopScan() bluetooth not enabled");
-        }
-
-        if (!adapter.isDiscovering()) {
-            return;
-        }
-
-        BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
-        long start = System.currentTimeMillis();
-        assertTrue(adapter.cancelDiscovery());
-
-        while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) {
-            if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
-                writeOutput(String.format("stopScan() completed in %d ms",
-                        (receiver.getCompletedTime() - start)));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
-                adapter.isDiscovering(), firedFlags, mask));
-
-    }
-
-    /**
-     * Enables PAN tethering on the local device and checks to make sure that tethering is enabled.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void enablePan(BluetoothAdapter adapter) {
-        if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-
-        long start = System.currentTimeMillis();
-        mPan.setBluetoothTethering(true);
-        long stop = System.currentTimeMillis();
-        assertTrue(mPan.isTetheringOn());
-
-        writeOutput(String.format("enablePan() completed in %d ms", (stop - start)));
-    }
-
-    /**
-     * Disables PAN tethering on the local device and checks to make sure that tethering is
-     * disabled.
-     *
-     * @param adapter The BT adapter.
-     */
-    public void disablePan(BluetoothAdapter adapter) {
-        if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-
-        long start = System.currentTimeMillis();
-        mPan.setBluetoothTethering(false);
-        long stop = System.currentTimeMillis();
-        assertFalse(mPan.isTetheringOn());
-
-        writeOutput(String.format("disablePan() completed in %d ms", (stop - start)));
-    }
-
-    /**
-     * Initiates a pairing with a remote device and checks to make sure that the devices are paired
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param passkey The pairing passkey if pairing requires a passkey. Any value if not.
-     * @param pin The pairing pin if pairing requires a pin. Any value if not.
-     */
-    public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) {
-        pairOrAcceptPair(adapter, device, passkey, pin, true);
-    }
-
-    /**
-     * Accepts a pairing with a remote device and checks to make sure that the devices are paired
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param passkey The pairing passkey if pairing requires a passkey. Any value if not.
-     * @param pin The pairing pin if pairing requires a pin. Any value if not.
-     */
-    public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey,
-            byte[] pin) {
-        pairOrAcceptPair(adapter, device, passkey, pin, false);
-    }
-
-    /**
-     * Helper method used by {@link #pair(BluetoothAdapter, BluetoothDevice, int, byte[])} and
-     * {@link #acceptPair(BluetoothAdapter, BluetoothDevice, int, byte[])} to either pair or accept
-     * a pairing request.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param passkey The pairing passkey if pairing requires a passkey. Any value if not.
-     * @param pin The pairing pin if pairing requires a pin. Any value if not.
-     * @param shouldPair Whether to pair or accept the pair.
-     */
-    private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey,
-            byte[] pin, boolean shouldPair) {
-        int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG;
-        long start = -1;
-        String methodName;
-        if (shouldPair) {
-            methodName = String.format("pair(device=%s)", device);
-        } else {
-            methodName = String.format("acceptPair(device=%s)", device);
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        PairReceiver receiver = getPairReceiver(device, passkey, pin, mask);
-
-        int state = device.getBondState();
-        switch (state) {
-            case BluetoothDevice.BOND_NONE:
-                assertFalse(adapter.getBondedDevices().contains(device));
-                start = System.currentTimeMillis();
-                if (shouldPair) {
-                    assertTrue(device.createBond());
-                }
-                break;
-            case BluetoothDevice.BOND_BONDING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            case BluetoothDevice.BOND_BONDED:
-                assertTrue(adapter.getBondedDevices().contains(device));
-                return;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) {
-            state = device.getBondState();
-            if (state == BluetoothDevice.BOND_BONDED && (receiver.getFiredFlags() & mask) == mask) {
-                assertTrue(adapter.getBondedDevices().contains(device));
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask));
-    }
-
-    /**
-     * Deletes a pairing with a remote device and checks to make sure that the devices are unpaired
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void unpair(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = PairReceiver.STATE_NONE_FLAG;
-        long start = -1;
-        String methodName = String.format("unpair(device=%s)", device);
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        PairReceiver receiver = getPairReceiver(device, 0, null, mask);
-
-        int state = device.getBondState();
-        switch (state) {
-            case BluetoothDevice.BOND_NONE:
-                assertFalse(adapter.getBondedDevices().contains(device));
-                removeReceiver(receiver);
-                return;
-            case BluetoothDevice.BOND_BONDING:
-                start = System.currentTimeMillis();
-                assertTrue(device.removeBond());
-                break;
-            case BluetoothDevice.BOND_BONDED:
-                assertTrue(adapter.getBondedDevices().contains(device));
-                start = System.currentTimeMillis();
-                assertTrue(device.removeBond());
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) {
-            if (device.getBondState() == BluetoothDevice.BOND_NONE
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                assertFalse(adapter.getBondedDevices().contains(device));
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask));
-    }
-
-    /**
-     * Deletes all pairings of remote devices
-     * @param adapter the BT adapter
-     */
-    public void unpairAll(BluetoothAdapter adapter) {
-        Set<BluetoothDevice> devices = adapter.getBondedDevices();
-        for (BluetoothDevice device : devices) {
-            unpair(adapter, device);
-        }
-    }
-
-    /**
-     * Connects a profile from the local device to a remote device and checks to make sure that the
-     * profile is connected and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}..
-     * @param methodName The method name to printed in the logs.  If null, will be
-     * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
-     */
-    public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile,
-            String methodName) {
-        if (methodName == null) {
-            methodName = String.format("connectProfile(profile=%d, device=%s)", profile, device);
-        }
-        int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG
-                | ConnectProfileReceiver.STATE_CONNECTED_FLAG);
-        long start = -1;
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        BluetoothProfile proxy = connectProxy(adapter, profile);
-        assertNotNull(proxy);
-
-        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
-
-        int state = proxy.getConnectionState(device);
-        switch (state) {
-            case BluetoothProfile.STATE_CONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothProfile.STATE_CONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            case BluetoothProfile.STATE_DISCONNECTED:
-            case BluetoothProfile.STATE_DISCONNECTING:
-                start = System.currentTimeMillis();
-                if (profile == BluetoothProfile.A2DP) {
-                    assertTrue(((BluetoothA2dp)proxy).connect(device));
-                } else if (profile == BluetoothProfile.HEADSET) {
-                    assertTrue(((BluetoothHeadset)proxy).connect(device));
-                } else if (profile == BluetoothProfile.HID_HOST) {
-                    assertTrue(((BluetoothHidHost)proxy).connect(device));
-                } else if (profile == BluetoothProfile.MAP_CLIENT) {
-                    assertTrue(((BluetoothMapClient)proxy).connect(device));
-                }
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = proxy.getConnectionState(device);
-            if (state == BluetoothProfile.STATE_CONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothProfile.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Disconnects a profile between the local device and a remote device and checks to make sure
-     * that the profile is disconnected and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
-     * @param methodName The method name to printed in the logs.  If null, will be
-     * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
-     */
-    public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile,
-            String methodName) {
-        if (methodName == null) {
-            methodName = String.format("disconnectProfile(profile=%d, device=%s)", profile, device);
-        }
-        int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG
-                | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG);
-        long start = -1;
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        BluetoothProfile proxy = connectProxy(adapter, profile);
-        assertNotNull(proxy);
-
-        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
-
-        int state = proxy.getConnectionState(device);
-        switch (state) {
-            case BluetoothProfile.STATE_CONNECTED:
-            case BluetoothProfile.STATE_CONNECTING:
-                start = System.currentTimeMillis();
-                if (profile == BluetoothProfile.A2DP) {
-                    assertTrue(((BluetoothA2dp)proxy).disconnect(device));
-                } else if (profile == BluetoothProfile.HEADSET) {
-                    assertTrue(((BluetoothHeadset)proxy).disconnect(device));
-                } else if (profile == BluetoothProfile.HID_HOST) {
-                    assertTrue(((BluetoothHidHost)proxy).disconnect(device));
-                } else if (profile == BluetoothProfile.MAP_CLIENT) {
-                    assertTrue(((BluetoothMapClient)proxy).disconnect(device));
-                }
-                break;
-            case BluetoothProfile.STATE_DISCONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothProfile.STATE_DISCONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = proxy.getConnectionState(device);
-            if (state == BluetoothProfile.STATE_DISCONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
-                methodName, state, BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Connects the PANU to a remote NAP and checks to make sure that the PANU is connected and that
-     * the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void connectPan(BluetoothAdapter adapter, BluetoothDevice device) {
-        connectPanOrIncomingPanConnection(adapter, device, true);
-    }
-
-    /**
-     * Checks that a remote PANU connects to the local NAP correctly and that the correct actions
-     * were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void incomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device) {
-        connectPanOrIncomingPanConnection(adapter, device, false);
-    }
-
-    /**
-     * Helper method used by {@link #connectPan(BluetoothAdapter, BluetoothDevice)} and
-     * {@link #incomingPanConnection(BluetoothAdapter, BluetoothDevice)} to either connect to a
-     * remote NAP or verify that a remote device connected to the local NAP.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param connect If the method should initiate the connection (is PANU)
-     */
-    private void connectPanOrIncomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device,
-            boolean connect) {
-        long start = -1;
-        int mask, role;
-        String methodName;
-
-        if (connect) {
-            methodName = String.format("connectPan(device=%s)", device);
-            mask = (ConnectProfileReceiver.STATE_CONNECTED_FLAG |
-                    ConnectProfileReceiver.STATE_CONNECTING_FLAG);
-            role = BluetoothPan.LOCAL_PANU_ROLE;
-        } else {
-            methodName = String.format("incomingPanConnection(device=%s)", device);
-            mask = ConnectProfileReceiver.STATE_CONNECTED_FLAG;
-            role = BluetoothPan.LOCAL_NAP_ROLE;
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-        ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask);
-
-        int state = mPan.getConnectionState(device);
-        switch (state) {
-            case BluetoothPan.STATE_CONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothPan.STATE_CONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            case BluetoothPan.STATE_DISCONNECTED:
-            case BluetoothPan.STATE_DISCONNECTING:
-                start = System.currentTimeMillis();
-                if (role == BluetoothPan.LOCAL_PANU_ROLE) {
-                    Log.i("BT", "connect to pan");
-                    assertTrue(mPan.connect(device));
-                }
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = mPan.getConnectionState(device);
-            if (state == BluetoothPan.STATE_CONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, state, BluetoothPan.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Disconnects the PANU from a remote NAP and checks to make sure that the PANU is disconnected
-     * and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void disconnectPan(BluetoothAdapter adapter, BluetoothDevice device) {
-        disconnectFromRemoteOrVerifyConnectNap(adapter, device, true);
-    }
-
-    /**
-     * Checks that a remote PANU disconnects from the local NAP correctly and that the correct
-     * actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void incomingPanDisconnection(BluetoothAdapter adapter, BluetoothDevice device) {
-        disconnectFromRemoteOrVerifyConnectNap(adapter, device, false);
-    }
-
-    /**
-     * Helper method used by {@link #disconnectPan(BluetoothAdapter, BluetoothDevice)} and
-     * {@link #incomingPanDisconnection(BluetoothAdapter, BluetoothDevice)} to either disconnect
-     * from a remote NAP or verify that a remote device disconnected from the local NAP.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param disconnect Whether the method should connect or verify.
-     */
-    private void disconnectFromRemoteOrVerifyConnectNap(BluetoothAdapter adapter,
-            BluetoothDevice device, boolean disconnect) {
-        long start = -1;
-        int mask, role;
-        String methodName;
-
-        if (disconnect) {
-            methodName = String.format("disconnectPan(device=%s)", device);
-            mask = (ConnectProfileReceiver.STATE_DISCONNECTED_FLAG |
-                    ConnectProfileReceiver.STATE_DISCONNECTING_FLAG);
-            role = BluetoothPan.LOCAL_PANU_ROLE;
-        } else {
-            methodName = String.format("incomingPanDisconnection(device=%s)", device);
-            mask = ConnectProfileReceiver.STATE_DISCONNECTED_FLAG;
-            role = BluetoothPan.LOCAL_NAP_ROLE;
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN);
-        assertNotNull(mPan);
-        ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask);
-
-        int state = mPan.getConnectionState(device);
-        switch (state) {
-            case BluetoothPan.STATE_CONNECTED:
-            case BluetoothPan.STATE_CONNECTING:
-                start = System.currentTimeMillis();
-                if (role == BluetoothPan.LOCAL_PANU_ROLE) {
-                    assertTrue(mPan.disconnect(device));
-                }
-                break;
-            case BluetoothPan.STATE_DISCONNECTED:
-                removeReceiver(receiver);
-                return;
-            case BluetoothPan.STATE_DISCONNECTING:
-                mask = 0; // Don't check for received intents since we might have missed them.
-                break;
-            default:
-                removeReceiver(receiver);
-                fail(String.format("%s invalid state: state=%d", methodName, state));
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
-            state = mPan.getConnectionState(device);
-            if (state == BluetoothHidHost.STATE_DISCONNECTED
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Opens a SCO channel using {@link android.media.AudioManager#startBluetoothSco()} and checks
-     * to make sure that the channel is opened and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void startSco(BluetoothAdapter adapter, BluetoothDevice device) {
-        startStopSco(adapter, device, true);
-    }
-
-    /**
-     * Closes a SCO channel using {@link android.media.AudioManager#stopBluetoothSco()} and checks
-     *  to make sure that the channel is closed and that the correct actions were broadcast.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     */
-    public void stopSco(BluetoothAdapter adapter, BluetoothDevice device) {
-        startStopSco(adapter, device, false);
-    }
-    /**
-     * Helper method for {@link #startSco(BluetoothAdapter, BluetoothDevice)} and
-     * {@link #stopSco(BluetoothAdapter, BluetoothDevice)}.
-     *
-     * @param adapter The BT adapter.
-     * @param device The remote device.
-     * @param isStart Whether the SCO channel should be opened.
-     */
-    private void startStopSco(BluetoothAdapter adapter, BluetoothDevice device, boolean isStart) {
-        long start = -1;
-        int mask;
-        String methodName;
-
-        if (isStart) {
-            methodName = String.format("startSco(device=%s)", device);
-            mask = StartStopScoReceiver.STATE_CONNECTED_FLAG;
-        } else {
-            methodName = String.format("stopSco(device=%s)", device);
-            mask = StartStopScoReceiver.STATE_DISCONNECTED_FLAG;
-        }
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        assertNotNull(manager);
-
-        if (!manager.isBluetoothScoAvailableOffCall()) {
-            fail(String.format("%s device does not support SCO", methodName));
-        }
-
-        boolean isScoOn = manager.isBluetoothScoOn();
-        if (isStart == isScoOn) {
-            return;
-        }
-
-        StartStopScoReceiver receiver = getStartStopScoReceiver(mask);
-        start = System.currentTimeMillis();
-        if (isStart) {
-            manager.startBluetoothSco();
-        } else {
-            manager.stopBluetoothSco();
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < START_STOP_SCO_TIMEOUT) {
-            isScoOn = manager.isBluetoothScoOn();
-            if (isStart == isScoOn && (receiver.getFiredFlags() & mask) == mask) {
-                long finish = receiver.getCompletedTime();
-                if (start != -1 && finish != -1) {
-                    writeOutput(String.format("%s completed in %d ms", methodName,
-                            (finish - start)));
-                } else {
-                    writeOutput(String.format("%s completed", methodName));
-                }
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: on=%b (expected %b), flags=0x%x (expected 0x%x)",
-                methodName, isScoOn, isStart, firedFlags, mask));
-    }
-
-    /**
-     * Writes a string to the logcat and a file if a file has been specified in the constructor.
-     *
-     * @param s The string to be written.
-     */
-    public void writeOutput(String s) {
-        Log.i(mTag, s);
-        if (mOutputWriter == null) {
-            return;
-        }
-        try {
-            mOutputWriter.write(s + "\n");
-            mOutputWriter.flush();
-        } catch (IOException e) {
-            Log.w(mTag, "Could not write to output file", e);
-        }
-    }
-
-    public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask;
-        String methodName = "getUnreadMessage";
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
-        assertNotNull(mMce);
-
-        if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
-            fail(String.format("%s device is not connected", methodName));
-        }
-
-        mMsgHandle = null;
-        mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG;
-        MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
-        assertTrue(mMce.getUnreadMessages(device));
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) {
-            if ((receiver.getFiredFlags() & mask) == mask) {
-                writeOutput(String.format("%s completed", methodName));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    /**
-     * Set a message to read/unread/deleted/undeleted
-     */
-    public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) {
-        int mask;
-        String methodName = "setMessageStatus";
-
-        if (!adapter.isEnabled()) {
-            fail(String.format("%s bluetooth not enabled", methodName));
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail(String.format("%s device not paired", methodName));
-        }
-
-        mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
-        assertNotNull(mMce);
-
-        if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
-            fail(String.format("%s device is not connected", methodName));
-        }
-
-        assertNotNull(mMsgHandle);
-        mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG;
-        MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
-
-        assertTrue(mMce.setMessageStatus(device, mMsgHandle, status));
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) {
-            if ((receiver.getFiredFlags() & mask) == mask) {
-                writeOutput(String.format("%s completed", methodName));
-                removeReceiver(receiver);
-                return;
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
-                methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask));
-    }
-
-    private void addReceiver(BroadcastReceiver receiver, String[] actions) {
-        IntentFilter filter = new IntentFilter();
-        for (String action: actions) {
-            filter.addAction(action);
-        }
-        mContext.registerReceiver(receiver, filter);
-        mReceivers.add(receiver);
-    }
-
-    private BluetoothReceiver getBluetoothReceiver(int expectedFlags) {
-        String[] actions = {
-                BluetoothAdapter.ACTION_DISCOVERY_FINISHED,
-                BluetoothAdapter.ACTION_DISCOVERY_STARTED,
-                BluetoothAdapter.ACTION_SCAN_MODE_CHANGED,
-                BluetoothAdapter.ACTION_STATE_CHANGED};
-        BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin,
-            int expectedFlags) {
-        String[] actions = {
-                BluetoothDevice.ACTION_PAIRING_REQUEST,
-                BluetoothDevice.ACTION_BOND_STATE_CHANGED};
-        PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile,
-            int expectedFlags) {
-        String[] actions = {
-                BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED};
-        ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
-                expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private ConnectPanReceiver getConnectPanReceiver(BluetoothDevice device, int role,
-            int expectedFlags) {
-        String[] actions = {BluetoothPan.ACTION_CONNECTION_STATE_CHANGED};
-        ConnectPanReceiver receiver = new ConnectPanReceiver(device, role, expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) {
-        String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED};
-        StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device,
-            int expectedFlags) {
-        String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED,
-            BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED,
-            BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED};
-        MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags);
-        addReceiver(receiver, actions);
-        return receiver;
-    }
-
-    private void removeReceiver(BroadcastReceiver receiver) {
-        mContext.unregisterReceiver(receiver);
-        mReceivers.remove(receiver);
-    }
-
-    private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) {
-        switch (profile) {
-            case BluetoothProfile.A2DP:
-                if (mA2dp != null) {
-                    return mA2dp;
-                }
-                break;
-            case BluetoothProfile.HEADSET:
-                if (mHeadset != null) {
-                    return mHeadset;
-                }
-                break;
-            case BluetoothProfile.HID_HOST:
-                if (mInput != null) {
-                    return mInput;
-                }
-                break;
-            case BluetoothProfile.PAN:
-                if (mPan != null) {
-                    return mPan;
-                }
-            case BluetoothProfile.MAP_CLIENT:
-                if (mMce != null) {
-                    return mMce;
-                }
-                break;
-            default:
-                return null;
-        }
-        adapter.getProfileProxy(mContext, mServiceListener, profile);
-        long s = System.currentTimeMillis();
-        switch (profile) {
-            case BluetoothProfile.A2DP:
-                while (mA2dp == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mA2dp;
-            case BluetoothProfile.HEADSET:
-                while (mHeadset == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mHeadset;
-            case BluetoothProfile.HID_HOST:
-                while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mInput;
-            case BluetoothProfile.PAN:
-                while (mPan == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mPan;
-            case BluetoothProfile.MAP_CLIENT:
-                while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
-                    sleep(POLL_TIME);
-                }
-                return mMce;
-            default:
-                return null;
-        }
-    }
-
-    private void sleep(long time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-        }
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
deleted file mode 100644
index 536d722..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth;
-
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link BluetoothUuid}.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class BluetoothUuidTest extends TestCase {
-
-    @SmallTest
-    public void testUuidParser() {
-        byte[] uuid16 = new byte[] {
-                0x0B, 0x11 };
-        assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
-                BluetoothUuid.parseUuidFrom(uuid16));
-
-        byte[] uuid32 = new byte[] {
-                0x0B, 0x11, 0x33, (byte) 0xFE };
-        assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"),
-                BluetoothUuid.parseUuidFrom(uuid32));
-
-        byte[] uuid128 = new byte[] {
-                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF };
-        assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"),
-                BluetoothUuid.parseUuidFrom(uuid128));
-    }
-
-    @SmallTest
-    public void testUuidType() {
-        assertTrue(BluetoothUuid.is16BitUuid(
-                ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB")));
-        assertFalse(BluetoothUuid.is32BitUuid(
-                ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB")));
-
-        assertFalse(BluetoothUuid.is16BitUuid(
-                ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB")));
-        assertTrue(BluetoothUuid.is32BitUuid(
-                ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB")));
-        assertFalse(BluetoothUuid.is32BitUuid(
-                ParcelUuid.fromString("FE33110B-1000-1000-8000-00805F9B34FB")));
-
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
deleted file mode 100644
index e58d905..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for {@link AdvertiseData}.
- * <p>
- * To run the test, use adb shell am instrument -e class 'android.bluetooth.le.AdvertiseDataTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class AdvertiseDataTest extends TestCase {
-
-    private AdvertiseData.Builder mAdvertiseDataBuilder;
-
-    @Override
-    protected void setUp() throws Exception {
-        mAdvertiseDataBuilder = new AdvertiseData.Builder();
-    }
-
-    @SmallTest
-    public void testEmptyData() {
-        Parcel parcel = Parcel.obtain();
-        AdvertiseData data = mAdvertiseDataBuilder.build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testEmptyServiceUuid() {
-        Parcel parcel = Parcel.obtain();
-        AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testEmptyManufacturerData() {
-        Parcel parcel = Parcel.obtain();
-        int manufacturerId = 50;
-        byte[] manufacturerData = new byte[0];
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addManufacturerData(manufacturerId, manufacturerData).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testEmptyServiceData() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        byte[] serviceData = new byte[0];
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceData(uuid, serviceData).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testServiceUuid() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceUuid(uuid).addServiceUuid(uuid2).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testManufacturerData() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-
-        int manufacturerId = 50;
-        byte[] manufacturerData = new byte[] {
-                (byte) 0xF0, 0x00, 0x02, 0x15 };
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceUuid(uuid).addServiceUuid(uuid2)
-                        .addManufacturerData(manufacturerId, manufacturerData).build();
-
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-
-    @SmallTest
-    public void testServiceData() {
-        Parcel parcel = Parcel.obtain();
-        ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        byte[] serviceData = new byte[] {
-                (byte) 0xF0, 0x00, 0x02, 0x15 };
-        AdvertiseData data =
-                mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .addServiceData(uuid, serviceData).build();
-        data.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        AdvertiseData dataFromParcel =
-                AdvertiseData.CREATOR.createFromParcel(parcel);
-        assertEquals(data, dataFromParcel);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
deleted file mode 100644
index 35da4bc..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanRecord;
-import android.os.Parcel;
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for Bluetooth LE scan filters.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class ScanFilterTest extends TestCase {
-
-    private static final String DEVICE_MAC = "01:02:03:04:05:AB";
-    private ScanResult mScanResult;
-    private ScanFilter.Builder mFilterBuilder;
-
-    @Override
-    protected void setUp() throws Exception {
-        byte[] scanRecord = new byte[] {
-                0x02, 0x01, 0x1a, // advertising flags
-                0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
-                0x04, 0x09, 0x50, 0x65, 0x64, // setName
-                0x02, 0x0A, (byte) 0xec, // tx power level
-                0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
-                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
-                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
-        };
-
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC);
-        mScanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord),
-                -10, 1397545200000000L);
-        mFilterBuilder = new ScanFilter.Builder();
-    }
-
-    @SmallTest
-    public void testsetNameFilter() {
-        ScanFilter filter = mFilterBuilder.setDeviceName("Ped").build();
-        assertTrue("setName filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setDeviceName("Pem").build();
-        assertFalse("setName filter fails", filter.matches(mScanResult));
-
-    }
-
-    @SmallTest
-    public void testDeviceFilter() {
-        ScanFilter filter = mFilterBuilder.setDeviceAddress(DEVICE_MAC).build();
-        assertTrue("device filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build();
-        assertFalse("device filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testsetServiceUuidFilter() {
-        ScanFilter filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build();
-        assertTrue("uuid filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
-        assertFalse("uuid filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder
-                .setServiceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
-                        ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF"))
-                .build();
-        assertTrue("uuid filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testsetServiceDataFilter() {
-        byte[] setServiceData = new byte[] {
-                0x50, 0x64 };
-        ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-        ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] emptyData = new byte[0];
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] prefixData = new byte[] {
-                0x50 };
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build();
-        assertTrue("service data filter fails", filter.matches(mScanResult));
-
-        byte[] nonMatchData = new byte[] {
-                0x51, 0x64 };
-        byte[] mask = new byte[] {
-                (byte) 0x00, (byte) 0xFF };
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build();
-        assertTrue("partial service data filter fails", filter.matches(mScanResult));
-
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build();
-        assertFalse("service data filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testManufacturerSpecificData() {
-        byte[] setManufacturerData = new byte[] {
-                0x02, 0x15 };
-        int manufacturerId = 0xE0;
-        ScanFilter filter =
-                mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build();
-        assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
-
-        byte[] emptyData = new byte[0];
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build();
-        assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
-
-        byte[] prefixData = new byte[] {
-                0x02 };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build();
-        assertTrue("manufacturer data filter fails", filter.matches(mScanResult));
-
-        // Test data mask
-        byte[] nonMatchData = new byte[] {
-                0x02, 0x14 };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build();
-        assertFalse("manufacturer data filter fails", filter.matches(mScanResult));
-        byte[] mask = new byte[] {
-                (byte) 0xFF, (byte) 0x00
-        };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build();
-        assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult));
-    }
-
-    @SmallTest
-    public void testReadWriteParcel() {
-        ScanFilter filter = mFilterBuilder.build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setDeviceName("Ped").build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceUuid(
-                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"),
-                ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] serviceData = new byte[] {
-                0x50, 0x64 };
-
-        ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] serviceDataMask = new byte[] {
-                (byte) 0xFF, (byte) 0xFF };
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData, serviceDataMask)
-                .build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] manufacturerData = new byte[] {
-                0x02, 0x15 };
-        int manufacturerId = 0xE0;
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build();
-        testReadWriteParcelForFilter(filter);
-
-        filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build();
-        testReadWriteParcelForFilter(filter);
-
-        byte[] manufacturerDataMask = new byte[] {
-                (byte) 0xFF, (byte) 0xFF
-        };
-        filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData,
-                manufacturerDataMask).build();
-        testReadWriteParcelForFilter(filter);
-    }
-
-    private void testReadWriteParcelForFilter(ScanFilter filter) {
-        Parcel parcel = Parcel.obtain();
-        filter.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        ScanFilter filterFromParcel =
-                ScanFilter.CREATOR.createFromParcel(parcel);
-        assertEquals(filter, filterFromParcel);
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
deleted file mode 100644
index 4e817d4..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.os.ParcelUuid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.util.HexDump;
-import com.android.modules.utils.BytesMatcher;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Predicate;
-
-/**
- * Unit test cases for {@link ScanRecord}.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanRecordTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class ScanRecordTest extends TestCase {
-    /**
-     * Example raw beacons captured from a Blue Charm BC011
-     */
-    private static final String RECORD_URL = "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
-    private static final String RECORD_UUID = "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
-    private static final String RECORD_TLM = "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000";
-    private static final String RECORD_IBEACON = "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000";
-
-    @SmallTest
-    public void testMatchesAnyField_Eddystone_Parser() {
-        final List<String> found = new ArrayList<>();
-        final Predicate<byte[]> matcher = (v) -> {
-            found.add(HexDump.toHexString(v));
-            return false;
-        };
-        ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_URL))
-                .matchesAnyField(matcher);
-
-        assertEquals(Arrays.asList(
-                "020106",
-                "0303AAFE",
-                "1716AAFE10EE01626C7565636861726D626561636F6E7300",
-                "09168020691E0EFE1355",
-                "1109426C7565436861726D5F313639363835"), found);
-    }
-
-    @SmallTest
-    public void testMatchesAnyField_Eddystone() {
-        final BytesMatcher matcher = BytesMatcher.decode("⊆0016AAFE/00FFFFFF");
-        assertMatchesAnyField(RECORD_URL, matcher);
-        assertMatchesAnyField(RECORD_UUID, matcher);
-        assertMatchesAnyField(RECORD_TLM, matcher);
-        assertNotMatchesAnyField(RECORD_IBEACON, matcher);
-    }
-
-    @SmallTest
-    public void testMatchesAnyField_iBeacon_Parser() {
-        final List<String> found = new ArrayList<>();
-        final Predicate<byte[]> matcher = (v) -> {
-            found.add(HexDump.toHexString(v));
-            return false;
-        };
-        ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_IBEACON))
-                .matchesAnyField(matcher);
-
-        assertEquals(Arrays.asList(
-                "020106",
-                "1AFF4C000215426C7565436861726D426561636F6E730EFE1355C5",
-                "09168020691E0EFE1355",
-                "1109426C7565436861726D5F313639363835"), found);
-    }
-
-    @SmallTest
-    public void testMatchesAnyField_iBeacon() {
-        final BytesMatcher matcher = BytesMatcher.decode("⊆00FF4C0002/00FFFFFFFF");
-        assertNotMatchesAnyField(RECORD_URL, matcher);
-        assertNotMatchesAnyField(RECORD_UUID, matcher);
-        assertNotMatchesAnyField(RECORD_TLM, matcher);
-        assertMatchesAnyField(RECORD_IBEACON, matcher);
-    }
-
-    @SmallTest
-    public void testParser() {
-        byte[] scanRecord = new byte[] {
-                0x02, 0x01, 0x1a, // advertising flags
-                0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
-                0x04, 0x09, 0x50, 0x65, 0x64, // name
-                0x02, 0x0A, (byte) 0xec, // tx power level
-                0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data
-                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
-                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
-        };
-        ScanRecord data = ScanRecord.parseFromBytes(scanRecord);
-        assertEquals(0x1a, data.getAdvertiseFlags());
-        ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-        ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-        assertTrue(data.getServiceUuids().contains(uuid1));
-        assertTrue(data.getServiceUuids().contains(uuid2));
-
-        assertEquals("Ped", data.getDeviceName());
-        assertEquals(-20, data.getTxPowerLevel());
-
-        assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null);
-        assertArrayEquals(new byte[] {
-                0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0));
-
-        assertTrue(data.getServiceData().containsKey(uuid2));
-        assertArrayEquals(new byte[] {
-                0x50, 0x64 }, data.getServiceData().get(uuid2));
-    }
-
-    // Assert two byte arrays are equal.
-    private static void assertArrayEquals(byte[] expected, byte[] actual) {
-        if (!Arrays.equals(expected, actual)) {
-            fail("expected:<" + Arrays.toString(expected) +
-                    "> but was:<" + Arrays.toString(actual) + ">");
-        }
-
-    }
-
-    private static void assertMatchesAnyField(String record, BytesMatcher matcher) {
-        assertTrue(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
-                .matchesAnyField(matcher));
-    }
-
-    private static void assertNotMatchesAnyField(String record, BytesMatcher matcher) {
-        assertFalse(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
-                .matchesAnyField(matcher));
-    }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
deleted file mode 100644
index 01d5c59..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Unit test cases for Bluetooth LE scans.
- * <p>
- * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanResultTest' -w
- * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
- */
-public class ScanResultTest extends TestCase {
-
-    /**
-     * Test read and write parcel of ScanResult
-     */
-    @SmallTest
-    public void testScanResultParceling() {
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
-                "01:02:03:04:05:06");
-        byte[] scanRecord = new byte[] {
-                1, 2, 3 };
-        int rssi = -10;
-        long timestampMicros = 10000L;
-
-        ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), rssi,
-                timestampMicros);
-        Parcel parcel = Parcel.obtain();
-        result.writeToParcel(parcel, 0);
-        // Need to reset parcel data position to the beginning.
-        parcel.setDataPosition(0);
-        ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel);
-        assertEquals(result, resultFromParcel);
-    }
-
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java
deleted file mode 100644
index 7c42c3b..0000000
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2014 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.bluetooth.le;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * Test for Bluetooth LE {@link ScanSettings}.
- */
-public class ScanSettingsTest extends TestCase {
-
-    @SmallTest
-    public void testCallbackType() {
-        ScanSettings.Builder builder = new ScanSettings.Builder();
-        builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
-        builder.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH);
-        builder.setCallbackType(ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-        builder.setCallbackType(
-                ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-        try {
-            builder.setCallbackType(
-                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES | ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-            fail("should have thrown IllegalArgumentException!");
-        } catch (IllegalArgumentException e) {
-            // nothing to do
-        }
-
-        try {
-            builder.setCallbackType(
-                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES |
-                    ScanSettings.CALLBACK_TYPE_FIRST_MATCH);
-            fail("should have thrown IllegalArgumentException!");
-        } catch (IllegalArgumentException e) {
-            // nothing to do
-        }
-
-        try {
-            builder.setCallbackType(
-                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES |
-                    ScanSettings.CALLBACK_TYPE_FIRST_MATCH |
-                    ScanSettings.CALLBACK_TYPE_MATCH_LOST);
-            fail("should have thrown IllegalArgumentException!");
-        } catch (IllegalArgumentException e) {
-            // nothing to do
-        }
-
-    }
-}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 0d2d047..a409129 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -176,7 +176,6 @@
     private InteractionJankMonitor createMockedInteractionJankMonitor() {
         InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
         doReturn(true).when(monitor).shouldMonitor(anyInt());
-        doNothing().when(monitor).notifyEvents(any(), any(), any());
         return monitor;
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 69e617a..9699275 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -83,7 +83,7 @@
         final Parcel parcel = Parcel.obtain();
         parcel.writeParcelable(outBatteryUsageStats, 0);
 
-        assertThat(parcel.dataSize()).isLessThan(5500);
+        assertThat(parcel.dataSize()).isLessThan(6000);
 
         parcel.setDataPosition(0);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index e7ce9a0..a787357 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -25,6 +25,8 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
 import android.os.WorkSource;
@@ -40,6 +42,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class WifiPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
 
@@ -66,14 +69,18 @@
         batteryStats.noteNetworkInterfaceForTransports("wifi",
                 new int[]{NetworkCapabilities.TRANSPORT_WIFI});
 
-        NetworkStats networkStats = new NetworkStats(10000, 1)
-                .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
-                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
-        mStatsRule.setNetworkStats(networkStats);
+        mStatsRule.setNetworkStats(buildNetworkStats(10000, 1000, 100, 2000, 20));
 
         return batteryStats;
     }
 
+    private NetworkStats buildNetworkStats(long elapsedRealtime, int rxBytes, int rxPackets,
+            int txBytes, int txPackets) {
+        return new NetworkStats(elapsedRealtime, 1)
+                .insertEntry("wifi", APP_UID, 0, 0, rxBytes, rxPackets, txBytes, txPackets, 100)
+                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+    }
+
     /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */
     private WifiActivityEnergyInfo setupPowerControllerBasedModelEnergyNumbersInfo() {
         return new WifiActivityEnergyInfo(10000,
@@ -115,6 +122,61 @@
     }
 
     @Test
+    public void testPowerControllerBasedModel_powerProfile_byProcessState() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+
+        mStatsRule.setTime(1000, 1000);
+
+        BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(2000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000),
+                POWER_DATA_UNAVAILABLE, 2000, 2000,
+                mNetworkStatsManager);
+
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+        mStatsRule.setNetworkStats(buildNetworkStats(4000, 5000, 200, 7000, 80));
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(4000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000),
+                POWER_DATA_UNAVAILABLE, 4000, 4000,
+                mNetworkStatsManager);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+                .powerProfileModeledOnly()
+                .includePowerModels()
+                .includeProcessStateData()
+                .build(), calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(12423);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(2.0214666);
+        assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+        final BatteryConsumer.Key foreground = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key background = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        final BatteryConsumer.Key fgs = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(1.1214666);
+        assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.9);
+        assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+    }
+
+    @Test
     public void testPowerControllerBasedModel_measured() {
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
@@ -148,6 +210,60 @@
                 .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
     }
 
+    @Test
+    public void testPowerControllerBasedModel_measured_byProcessState() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+
+        mStatsRule.setTime(1000, 1000);
+
+        BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(2000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000),
+                1_000_000, 2000, 2000,
+                mNetworkStatsManager);
+
+        uid.setProcessStateForTest(
+                BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+        mStatsRule.setNetworkStats(buildNetworkStats(4000, 5000, 200, 7000, 80));
+
+        batteryStats.updateWifiState(new WifiActivityEnergyInfo(4000,
+                        WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000),
+                5_000_000, 4000, 4000,
+                mNetworkStatsManager);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+                .includePowerModels()
+                .includeProcessStateData()
+                .build(), calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(12423);
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(1.0325211);
+        assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+        final BatteryConsumer.Key foreground = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key background = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        final BatteryConsumer.Key fgs = uidConsumer.getKey(
+                BatteryConsumer.POWER_COMPONENT_WIFI,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(0.5517519);
+        assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.4807691);
+        assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+    }
+
     /** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */
     private BatteryStatsImpl setupTimerBasedModelTestNumbers() {
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/DeviceFeaturesTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/DeviceFeaturesTest.java
new file mode 100644
index 0000000..875ab38
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/DeviceFeaturesTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.hardware.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link DeviceFeatures} */
+@RunWith(JUnit4.class)
+@SmallTest
+public class DeviceFeaturesTest {
+
+    @Test
+    public void testEquals() {
+        new EqualsTester()
+                .addEqualityGroup(DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN)
+                .addEqualityGroup(DeviceFeatures.NO_FEATURES_SUPPORTED)
+                .addEqualityGroup(
+                        DeviceFeatures.fromOperand(
+                                new byte[]{(byte) 0b0111_0000}),
+                        DeviceFeatures.fromOperand(
+                                new byte[]{(byte) 0b1111_0000}),
+                        DeviceFeatures.fromOperand(
+                                new byte[]{(byte) 0b1111_0000, (byte) 0b0101_0101}),
+                        DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                                .setRecordTvScreenSupport(FEATURE_SUPPORTED)
+                                .setSetOsdStringSupport(FEATURE_SUPPORTED)
+                                .setDeckControlSupport(FEATURE_SUPPORTED)
+                                .setSetAudioRateSupport(FEATURE_NOT_SUPPORTED)
+                                .setArcTxSupport(FEATURE_NOT_SUPPORTED)
+                                .setArcRxSupport(FEATURE_NOT_SUPPORTED)
+                                .setSetAudioVolumeLevelSupport(FEATURE_NOT_SUPPORTED)
+                                .build()
+                )
+                .testEquals();
+    }
+
+    @Test
+    public void testDeviceFeaturesOperandConversion() {
+        DeviceFeatures info = DeviceFeatures.fromOperand(
+                new byte[]{(byte) 0b0111_0000});
+
+        assertThat(info.getRecordTvScreenSupport()).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(info.getSetOsdStringSupport()).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(info.getDeckControlSupport()).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(info.getSetAudioRateSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(info.getArcTxSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(info.getArcRxSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(info.getSetAudioVolumeLevelSupport()).isEqualTo(FEATURE_NOT_SUPPORTED);
+
+        assertThat(info.toOperand()).isEqualTo(new byte[]{(byte) 0b0111_0000});
+    }
+
+    @Test
+    public void testUpdate() {
+        DeviceFeatures oldFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_SUPPORTED)
+                .setSetOsdStringSupport(FEATURE_SUPPORTED)
+                .setDeckControlSupport(FEATURE_NOT_SUPPORTED)
+                .setSetAudioRateSupport(FEATURE_NOT_SUPPORTED)
+                .setArcTxSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setArcRxSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORT_UNKNOWN)
+                .build();
+
+        DeviceFeatures newFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_NOT_SUPPORTED)
+                .setSetOsdStringSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setDeckControlSupport(FEATURE_SUPPORTED)
+                .setSetAudioRateSupport(FEATURE_SUPPORT_UNKNOWN)
+                .setArcTxSupport(FEATURE_SUPPORTED)
+                .setArcRxSupport(FEATURE_NOT_SUPPORTED)
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORT_UNKNOWN)
+                .build();
+
+        // Always take the field from newFeatures, unless it's FEATURE_SUPPORT_UNKNOWN
+        DeviceFeatures updatedFeatures = DeviceFeatures.ALL_FEATURES_SUPPORT_UNKNOWN.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_NOT_SUPPORTED)
+                .setSetOsdStringSupport(FEATURE_SUPPORTED)
+                .setDeckControlSupport(FEATURE_SUPPORTED)
+                .setSetAudioRateSupport(FEATURE_NOT_SUPPORTED)
+                .setArcTxSupport(FEATURE_SUPPORTED)
+                .setArcRxSupport(FEATURE_NOT_SUPPORTED)
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORT_UNKNOWN)
+                .build();
+
+        assertThat(oldFeatures.toBuilder().update(newFeatures).build()).isEqualTo(updatedFeatures);
+    }
+}
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
index 4ce072c..5f7468e 100755
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
@@ -43,44 +43,32 @@
         int adopterId = 2;
 
         new EqualsTester()
-                .addEqualityGroup(new HdmiDeviceInfo())
+                .addEqualityGroup(HdmiDeviceInfo.INACTIVE_DEVICE)
                 .addEqualityGroup(
-                        new HdmiDeviceInfo(phyAddr, portId), new HdmiDeviceInfo(phyAddr, portId))
+                        HdmiDeviceInfo.hardwarePort(phyAddr, portId),
+                        HdmiDeviceInfo.hardwarePort(phyAddr, portId))
                 .addEqualityGroup(
-                        new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId),
-                        new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId))
+                        HdmiDeviceInfo.mhlDevice(phyAddr, portId, adopterId, deviceId),
+                        HdmiDeviceInfo.mhlDevice(phyAddr, portId, adopterId, deviceId))
                 .addEqualityGroup(
-                        new HdmiDeviceInfo(
-                                logicalAddr, phyAddr, portId, deviceType, vendorId, displayName),
-                        new HdmiDeviceInfo(
-                                logicalAddr, phyAddr, portId, deviceType, vendorId, displayName))
-                .addEqualityGroup(
-                        new HdmiDeviceInfo(
-                                logicalAddr,
-                                phyAddr,
-                                portId,
-                                deviceType,
-                                vendorId,
-                                displayName,
-                                powerStatus),
-                        new HdmiDeviceInfo(
-                                logicalAddr,
-                                phyAddr,
-                                portId,
-                                deviceType,
-                                vendorId,
-                                displayName,
-                                powerStatus))
-                .addEqualityGroup(
-                        new HdmiDeviceInfo(
-                                logicalAddr,
-                                phyAddr,
-                                portId,
-                                deviceType,
-                                vendorId,
-                                displayName,
-                                powerStatus,
-                                cecVersion))
+                        HdmiDeviceInfo.cecDeviceBuilder()
+                                .setLogicalAddress(logicalAddr)
+                                .setPhysicalAddress(phyAddr)
+                                .setPortId(portId)
+                                .setDeviceType(deviceType)
+                                .setVendorId(vendorId)
+                                .setDisplayName(displayName)
+                                .setDevicePowerStatus(powerStatus)
+                                .setCecVersion(cecVersion).build(),
+                        HdmiDeviceInfo.cecDeviceBuilder()
+                                .setLogicalAddress(logicalAddr)
+                                .setPhysicalAddress(phyAddr)
+                                .setPortId(portId)
+                                .setDeviceType(deviceType)
+                                .setVendorId(vendorId)
+                                .setDisplayName(displayName)
+                                .setDevicePowerStatus(powerStatus)
+                                .setCecVersion(cecVersion).build())
                 .testEquals();
     }
 }
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index 50e8474..b659f37 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -21,13 +21,16 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
+import android.os.RemoteException;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
@@ -38,12 +41,16 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.nio.charset.StandardCharsets;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LockPatternUtilsTest {
@@ -102,4 +109,84 @@
         configureTest(false, true, 0);
         assertFalse(mLockPatternUtils.isLockScreenDisabled(DEMO_USER_ID));
     }
+
+    @Test
+    public void testAddWeakEscrowToken() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        byte[] testToken = "test_token".getBytes(StandardCharsets.UTF_8);
+        int testUserId = 10;
+        IWeakEscrowTokenActivatedListener listener = createWeakEscrowTokenListener();
+        mLockPatternUtils.addWeakEscrowToken(testToken, testUserId, listener);
+        verify(ils).addWeakEscrowToken(eq(testToken), eq(testUserId), eq(listener));
+    }
+
+    @Test
+    public void testRegisterWeakEscrowTokenRemovedListener() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        IWeakEscrowTokenRemovedListener testListener = createTestAutoEscrowTokenRemovedListener();
+        mLockPatternUtils.registerWeakEscrowTokenRemovedListener(testListener);
+        verify(ils).registerWeakEscrowTokenRemovedListener(eq(testListener));
+    }
+
+    @Test
+    public void testUnregisterWeakEscrowTokenRemovedListener() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        IWeakEscrowTokenRemovedListener testListener = createTestAutoEscrowTokenRemovedListener();
+        mLockPatternUtils.unregisterWeakEscrowTokenRemovedListener(testListener);
+        verify(ils).unregisterWeakEscrowTokenRemovedListener(eq(testListener));
+    }
+
+    @Test
+    public void testRemoveAutoEscrowToken() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        int testUserId = 10;
+        long testHandle = 100L;
+        mLockPatternUtils.removeWeakEscrowToken(testHandle, testUserId);
+        verify(ils).removeWeakEscrowToken(eq(testHandle), eq(testUserId));
+    }
+
+    @Test
+    public void testIsAutoEscrowTokenActive() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        int testUserId = 10;
+        long testHandle = 100L;
+        mLockPatternUtils.isWeakEscrowTokenActive(testHandle, testUserId);
+        verify(ils).isWeakEscrowTokenActive(eq(testHandle), eq(testUserId));
+    }
+
+    @Test
+    public void testIsAutoEscrowTokenValid() throws RemoteException {
+        ILockSettings ils = createTestLockSettings();
+        int testUserId = 10;
+        byte[] testToken = "test_token".getBytes(StandardCharsets.UTF_8);
+        long testHandle = 100L;
+        mLockPatternUtils.isWeakEscrowTokenValid(testHandle, testToken, testUserId);
+        verify(ils).isWeakEscrowTokenValid(eq(testHandle), eq(testToken), eq(testUserId));
+    }
+
+    private ILockSettings createTestLockSettings() {
+        final Context context = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+        mLockPatternUtils = spy(new LockPatternUtils(context));
+        final ILockSettings ils = Mockito.mock(ILockSettings.class);
+        when(mLockPatternUtils.getLockSettings()).thenReturn(ils);
+        return ils;
+    }
+
+    private IWeakEscrowTokenActivatedListener createWeakEscrowTokenListener() {
+        return new IWeakEscrowTokenActivatedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenActivated(long handle, int userId) {
+                // Do nothing.
+            }
+        };
+    }
+
+    private IWeakEscrowTokenRemovedListener createTestAutoEscrowTokenRemovedListener() {
+        return new IWeakEscrowTokenRemovedListener.Stub() {
+            @Override
+            public void onWeakEscrowTokenRemoved(long handle, int userId) {
+                // Do nothing.
+            }
+        };
+    }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ee0fb44..6f5951b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -392,6 +392,7 @@
         <permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/>
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
+        <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
         <!-- Permissions required for Incremental CTS tests -->
         <permission name="com.android.permission.USE_INSTALLER_V2"/>
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index e6ff187..43cb5ee 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -16,8 +16,12 @@
 
 package android.graphics;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
  * mirrored by setting the tiling mode.
@@ -31,6 +35,47 @@
     private int mTileX;
     private int mTileY;
 
+    /** @hide */
+    @IntDef(prefix = {"FILTER_MODE"}, value = {
+            FILTER_MODE_DEFAULT,
+            FILTER_MODE_NEAREST,
+            FILTER_MODE_LINEAR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FilterMode {}
+
+    /**
+     * This FilterMode value will respect the value of the Paint#isFilterBitmap flag while the
+     * shader is attached to the Paint.
+     *
+     * <p>The exception to this rule is when a Shader is attached as input to a RuntimeShader. In
+     *    that case this mode will default to FILTER_MODE_NEAREST.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_DEFAULT = 0;
+    /**
+     * This FilterMode value will cause the shader to sample from the nearest pixel to the requested
+     * sample point.
+     *
+     * <p>This value will override the effect of Paint#isFilterBitmap.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_NEAREST = 1;
+    /**
+     * This FilterMode value will cause the shader to interpolate the output of the shader from a
+     * 2x2 grid of pixels nearest to the sample point (i.e. bilinear interpolation).
+     *
+     * <p>This value will override the effect of Paint#isFilterBitmap.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_LINEAR = 2;
+
+    @FilterMode
+    private int mFilterMode;
+
     /*
      *  This is cache of the last value from the Paint of bitmap-filtering.
      *  In the future, BitmapShaders will carry their own (expanded) data for this
@@ -49,6 +94,15 @@
     private boolean mFilterFromPaint;
 
     /**
+     *  Stores whether or not the contents of this shader's bitmap will be sampled
+     *  without modification or if the bitmap's properties, like colorspace and
+     *  premultiplied alpha, will be respected when sampling from the bitmap's buffer.
+     */
+    private boolean mIsDirectSampled;
+
+    private boolean mRequestDirectSampling;
+
+    /**
      * Call this to create a new shader that will draw with a bitmap.
      *
      * @param bitmap The bitmap to use inside the shader
@@ -66,24 +120,60 @@
         mBitmap = bitmap;
         mTileX = tileX;
         mTileY = tileY;
+        mFilterMode = FILTER_MODE_DEFAULT;
         mFilterFromPaint = false;
+        mIsDirectSampled = false;
+        mRequestDirectSampling = false;
+    }
+
+    /**
+     * Returns the filter mode used when sampling from this shader
+     */
+    @FilterMode
+    public int getFilterMode() {
+        return mFilterMode;
+    }
+
+    /**
+     * Set the filter mode to be used when sampling from this shader
+     */
+    public void setFilterMode(@FilterMode int mode) {
+        if (mode != mFilterMode) {
+            mFilterMode = mode;
+            discardNativeInstance();
+        }
+    }
+
+    /** @hide */
+    /* package */ synchronized long getNativeInstanceWithDirectSampling() {
+        mRequestDirectSampling = true;
+        return getNativeInstance();
     }
 
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
-        mFilterFromPaint = filterFromPaint;
+        boolean enableLinearFilter = mFilterMode == FILTER_MODE_LINEAR;
+        if (mFilterMode == FILTER_MODE_DEFAULT) {
+            mFilterFromPaint = filterFromPaint;
+            enableLinearFilter = mFilterFromPaint;
+        }
+
+        mIsDirectSampled = mRequestDirectSampling;
+        mRequestDirectSampling = false;
+
         return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, mTileY,
-                            mFilterFromPaint);
+                            enableLinearFilter, mIsDirectSampled);
     }
 
     /** @hide */
     @Override
     protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) {
-        return mFilterFromPaint != filterFromPaint;
+        return mIsDirectSampled != mRequestDirectSampling
+                || (mFilterMode == FILTER_MODE_DEFAULT && mFilterFromPaint != filterFromPaint);
     }
 
     private static native long nativeCreate(long nativeMatrix, long bitmapHandle,
-            int shaderTileModeX, int shaderTileModeY, boolean filter);
+            int shaderTileModeX, int shaderTileModeY, boolean filter, boolean isDirectSampled);
 }
 
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index ef57f4a..57046f5 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -279,11 +279,11 @@
     }
 
     /**
-     * Sets the uniform shader that is declares as input to this shader.  If the shader does not
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
      * have a uniform shader with that name then an IllegalArgumentException is thrown.
      *
-     * @param shaderName name matching the uniform declared in the SKSL shader
-     * @param shader shader passed into the SKSL shader for sampling
+     * @param shaderName name matching the uniform declared in the AGSL shader program
+     * @param shader shader passed into the AGSL shader program for sampling
      */
     public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
         if (shaderName == null) {
@@ -297,6 +297,28 @@
         discardNativeInstance();
     }
 
+    /**
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
+     * have a uniform shader with that name then an IllegalArgumentException is thrown.
+     *
+     * Unlike setInputShader this method returns samples directly from the bitmap's buffer. This
+     * means that there will be no transformation of the sampled pixels, such as colorspace
+     * conversion or alpha premultiplication.
+     */
+    public void setInputBuffer(@NonNull String shaderName, @NonNull BitmapShader shader) {
+        if (shaderName == null) {
+            throw new NullPointerException("The shaderName parameter must not be null");
+        }
+        if (shader == null) {
+            throw new NullPointerException("The shader parameter must not be null");
+        }
+
+        nativeUpdateShader(mNativeInstanceRuntimeShaderBuilder, shaderName,
+                shader.getNativeInstanceWithDirectSampling());
+        discardNativeInstance();
+    }
+
+
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
diff --git a/identity/java/android/security/identity/CredentialDataRequest.java b/identity/java/android/security/identity/CredentialDataRequest.java
new file mode 100644
index 0000000..2a47a02
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataRequest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An object representing a request for credential data.
+ */
+public class CredentialDataRequest {
+    CredentialDataRequest() {}
+
+    /**
+     * Gets the device-signed entries to request.
+     *
+     * @return the device-signed entries to request.
+     */
+    public @NonNull Map<String, Collection<String>> getDeviceSignedEntriesToRequest() {
+        return mDeviceSignedEntriesToRequest;
+    }
+
+    /**
+     * Gets the issuer-signed entries to request.
+     *
+     * @return the issuer-signed entries to request.
+     */
+    public @NonNull Map<String, Collection<String>> getIssuerSignedEntriesToRequest() {
+        return mIssuerSignedEntriesToRequest;
+    }
+
+    /**
+     * Gets whether to allow using an authentication key which use count has been exceeded.
+     *
+     * <p>By default this is set to true.
+     *
+     * @return whether to allow using an authentication key which use
+     *         count has been exceeded if no other key is available.
+     */
+    public boolean isAllowUsingExhaustedKeys() {
+        return mAllowUsingExhaustedKeys;
+    }
+
+    /**
+     * Gets whether to allow using an authentication key which is expired.
+     *
+     * <p>By default this is set to false.
+     *
+     * @return whether to allow using an authentication key which is
+     *         expired if no other key is available.
+     */
+    public boolean isAllowUsingExpiredKeys() {
+        return mAllowUsingExpiredKeys;
+    }
+
+    /**
+     * Gets whether to increment the use-count for the authentication key used.
+     *
+     * <p>By default this is set to true.
+     *
+     * @return whether to increment the use count of the authentication key used.
+     */
+    public boolean isIncrementUseCount() {
+        return mIncrementUseCount;
+    }
+
+    /**
+     * Gets the request message CBOR.
+     *
+     * <p>This data structure is described in the documentation for the
+     * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+     *
+     * @return the request message CBOR as described above.
+     */
+    public @Nullable byte[] getRequestMessage() {
+        return mRequestMessage;
+    }
+
+    /**
+     * Gets the reader signature.
+     *
+     * <p>This data structure is described in the documentation for the
+     * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+     *
+     * @return a {@code COSE_Sign1} structure as described above.
+     */
+    public @Nullable byte[] getReaderSignature() {
+        return mReaderSignature;
+    }
+
+    Map<String, Collection<String>> mDeviceSignedEntriesToRequest = new LinkedHashMap<>();
+    Map<String, Collection<String>> mIssuerSignedEntriesToRequest = new LinkedHashMap<>();
+    boolean mAllowUsingExhaustedKeys = true;
+    boolean mAllowUsingExpiredKeys = false;
+    boolean mIncrementUseCount = true;
+    byte[] mRequestMessage = null;
+    byte[] mReaderSignature = null;
+
+    /**
+     * A builder for {@link CredentialDataRequest}.
+     */
+    public static final class Builder {
+        private CredentialDataRequest mData;
+
+        /**
+         * Creates a new builder.
+         */
+        public Builder() {
+            mData = new CredentialDataRequest();
+        }
+
+        /**
+         * Sets the device-signed entries to request.
+         *
+         * @param entriesToRequest the device-signed entries to request.
+         */
+        public @NonNull Builder setDeviceSignedEntriesToRequest(
+                @NonNull Map<String, Collection<String>> entriesToRequest) {
+            mData.mDeviceSignedEntriesToRequest = entriesToRequest;
+            return this;
+        }
+
+        /**
+         * Sets the issuer-signed entries to request.
+         *
+         * @param entriesToRequest the issuer-signed entries to request.
+         * @return the builder.
+         */
+        public @NonNull Builder setIssuerSignedEntriesToRequest(
+                @NonNull Map<String, Collection<String>> entriesToRequest) {
+            mData.mIssuerSignedEntriesToRequest = entriesToRequest;
+            return this;
+        }
+
+        /**
+         * Sets whether to allow using an authentication key which use count has been exceeded.
+         *
+         * By default this is set to true.
+         *
+         * @param allowUsingExhaustedKeys whether to allow using an authentication key which use
+         *                                count has been exceeded if no other key is available.
+         * @return the builder.
+         */
+        public @NonNull Builder setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+            mData.mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+            return this;
+        }
+
+        /**
+         * Sets whether to allow using an authentication key which is expired.
+         *
+         * By default this is set to false.
+         *
+         * @param allowUsingExpiredKeys whether to allow using an authentication key which is
+         *                              expired if no other key is available.
+         * @return the builder.
+         */
+        public @NonNull Builder setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+            mData.mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+            return this;
+        }
+
+        /**
+         * Sets whether to increment the use-count for the authentication key used.
+         *
+         * By default this is set to true.
+         *
+         * @param incrementUseCount whether to increment the use count of the authentication
+         *                          key used.
+         * @return the builder.
+         */
+        public @NonNull Builder setIncrementUseCount(boolean incrementUseCount) {
+            mData.mIncrementUseCount = incrementUseCount;
+            return this;
+        }
+
+        /**
+         * Sets the request message CBOR.
+         *
+         * <p>This data structure is described in the documentation for the
+         * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+         *
+         * @param requestMessage the request message CBOR as described above.
+         * @return the builder.
+         */
+        public @NonNull Builder setRequestMessage(@NonNull byte[] requestMessage) {
+            mData.mRequestMessage = requestMessage;
+            return this;
+        }
+
+        /**
+         * Sets the reader signature.
+         *
+         * <p>This data structure is described in the documentation for the
+         * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+         *
+         * @param readerSignature a {@code COSE_Sign1} structure as described above.
+         * @return the builder.
+         */
+        public @NonNull Builder setReaderSignature(@NonNull byte[] readerSignature) {
+            mData.mReaderSignature = readerSignature;
+            return this;
+        }
+
+        /**
+         * Finishes building a {@link CredentialDataRequest}.
+         *
+         * @return the {@link CredentialDataRequest} object.
+         */
+        public @NonNull CredentialDataRequest build() {
+            return mData;
+        }
+    }
+}
diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java
new file mode 100644
index 0000000..beb03af
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataResult.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.util.Collection;
+
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested in a {@link PresentationSession}.
+ */
+public abstract class CredentialDataResult {
+    /**
+     * @hide
+     */
+    protected CredentialDataResult() {}
+
+    /**
+     * Returns a CBOR structure containing the retrieved device-signed data.
+     *
+     * <p>This structure - along with the session transcript - may be cryptographically
+     * authenticated to prove to the reader that the data is from a trusted credential and
+     * {@link #getDeviceMac()} can be used to get a MAC.
+     *
+     * <p>The CBOR structure which is cryptographically authenticated is the
+     * {@code DeviceAuthenticationBytes} structure according to the following
+     * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
+     *
+     * <pre>
+     *   DeviceAuthentication = [
+     *     "DeviceAuthentication",
+     *     SessionTranscript,
+     *     DocType,
+     *     DeviceNameSpacesBytes
+     *   ]
+     *
+     *   DocType = tstr
+     *   SessionTranscript = any
+     *   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+     *   DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+     * </pre>
+     *
+     * <p>where
+     *
+     * <pre>
+     *   DeviceNameSpaces = {
+     *     * NameSpace => DeviceSignedItems
+     *   }
+     *
+     *   DeviceSignedItems = {
+     *     + DataItemName => DataItemValue
+     *   }
+     *
+     *   NameSpace = tstr
+     *   DataItemName = tstr
+     *   DataItemValue = any
+     * </pre>
+     *
+     * <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
+     * as defined above.
+     *
+     * @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
+     */
+    public abstract @NonNull byte[] getDeviceNameSpaces();
+
+    /**
+     * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
+     * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
+     * is from a trusted credential.
+     *
+     * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
+     * produced by using the key agreement and key derivation function from the ciphersuite
+     * with the authentication private key and the reader ephemeral public key to compute a
+     * shared message authentication code (MAC) key, then using the MAC function from the
+     * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
+     * ISO/IEC 18013-5 for details of this operation.
+     *
+     * <p>If the session transcript or reader ephemeral key wasn't set on the {@link
+     * PresentationSession} used to obtain this data no message authencation code will be produced
+     * and this method will return {@code null}.
+     *
+     * @return A COSE_Mac0 structure with the message authentication code as described above
+     *         or {@code null} if the conditions specified above are not met.
+     */
+    public abstract @Nullable byte[] getDeviceMac();
+
+    /**
+     * Returns the static authentication data associated with the dynamic authentication
+     * key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
+     *
+     * @return The static authentication data associated with dynamic authentication key used to
+     * MAC the data.
+     */
+    public abstract @NonNull byte[] getStaticAuthenticationData();
+
+    /**
+     * Gets the device-signed entries that was returned.
+     *
+     * @return an object to examine the entries returned.
+     */
+    public abstract @NonNull Entries getDeviceSignedEntries();
+
+    /**
+     * Gets the issuer-signed entries that was returned.
+     *
+     * @return an object to examine the entries returned.
+     */
+    public abstract @NonNull Entries getIssuerSignedEntries();
+
+    /**
+     * A class for representing data elements returned.
+     */
+    public interface Entries {
+        /** Value was successfully retrieved. */
+        int STATUS_OK = 0;
+
+        /** The entry does not exist. */
+        int STATUS_NO_SUCH_ENTRY = 1;
+
+        /** The entry was not requested. */
+        int STATUS_NOT_REQUESTED = 2;
+
+        /** The entry wasn't in the request message. */
+        int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+        /** The entry was not retrieved because user authentication failed. */
+        int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+        /** The entry was not retrieved because reader authentication failed. */
+        int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+        /**
+         * The entry was not retrieved because it was configured without any access
+         * control profile.
+         */
+        int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+        /**
+         * Gets the names of namespaces with retrieved entries.
+         *
+         * @return collection of name of namespaces containing retrieved entries. May be empty if no
+         *     data was retrieved.
+         */
+        @NonNull Collection<String> getNamespaces();
+
+        /**
+         * Get the names of all requested entries in a name space.
+         *
+         * <p>This includes the name of entries that wasn't successfully retrieved.
+         *
+         * @param namespaceName the namespace name to get entries for.
+         * @return A collection of names for the given namespace or the empty collection if no
+         *   entries was returned for the given name space.
+         */
+        @NonNull Collection<String> getEntryNames(@NonNull String namespaceName);
+
+        /**
+         * Get the names of all entries that was successfully retrieved from a name space.
+         *
+         * <p>This only return entries for which {@link #getStatus(String, String)} will return
+         * {@link #STATUS_OK}.
+         *
+         * @param namespaceName the namespace name to get entries for.
+         * @return The entries in the given namespace that were successfully rerieved or the
+         *   empty collection if no entries was returned for the given name space.
+         */
+        @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName);
+
+        /**
+         * Gets the status of an entry.
+         *
+         * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link
+         * #STATUS_NO_SUCH_ENTRY} if the given entry wasn't retrieved, {@link
+         * #STATUS_NOT_REQUESTED} if it wasn't requested, {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if
+         * the request message was set but the entry wasn't present in the request message, {@link
+         * #STATUS_USER_AUTHENTICATION_FAILED} if the value wasn't retrieved because the necessary
+         * user authentication wasn't performed, {@link #STATUS_READER_AUTHENTICATION_FAILED} if
+         * the supplied reader certificate chain didn't match the set of certificates the entry was
+         * provisioned with, or {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was
+         * configured without any access control profiles.
+         *
+         * @param namespaceName the namespace name of the entry.
+         * @param name the name of the entry to get the value for.
+         * @return the status indicating whether the value was retrieved and if not, why.
+         */
+        @Status int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+        /**
+         * Gets the raw CBOR data for the value of an entry.
+         *
+         * <p>This should only be called on an entry for which the {@link #getStatus(String,
+         * String)} method returns {@link #STATUS_OK}.
+         *
+         * @param namespaceName the namespace name of the entry.
+         * @param name the name of the entry to get the value for.
+         * @return the raw CBOR data or {@code null} if no entry with the given name exists.
+         */
+        @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
+
+        /**
+         * The type of the entry status.
+         * @hide
+         */
+        @Retention(SOURCE)
+        @IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED,
+                    STATUS_NOT_IN_REQUEST_MESSAGE, STATUS_USER_AUTHENTICATION_FAILED,
+                    STATUS_READER_AUTHENTICATION_FAILED, STATUS_NO_ACCESS_CONTROL_PROFILES})
+                    @interface Status {}
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
new file mode 100644
index 0000000..7afe3d4
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreCredentialDataResult extends CredentialDataResult {
+
+    ResultData mDeviceSignedResult;
+    ResultData mIssuerSignedResult;
+    CredstoreEntries mDeviceSignedEntries;
+    CredstoreEntries mIssuerSignedEntries;
+
+    CredstoreCredentialDataResult(ResultData deviceSignedResult, ResultData issuerSignedResult) {
+        mDeviceSignedResult = deviceSignedResult;
+        mIssuerSignedResult = issuerSignedResult;
+        mDeviceSignedEntries = new CredstoreEntries(deviceSignedResult);
+        mIssuerSignedEntries = new CredstoreEntries(issuerSignedResult);
+    }
+
+    @Override
+    public @NonNull byte[] getDeviceNameSpaces() {
+        return mDeviceSignedResult.getAuthenticatedData();
+    }
+
+    @Override
+    public @Nullable byte[] getDeviceMac() {
+        return mDeviceSignedResult.getMessageAuthenticationCode();
+    }
+
+    @Override
+    public @NonNull byte[] getStaticAuthenticationData() {
+        return mDeviceSignedResult.getStaticAuthenticationData();
+    }
+
+    @Override
+    public @NonNull CredentialDataResult.Entries getDeviceSignedEntries() {
+        return mDeviceSignedEntries;
+    }
+
+    @Override
+    public @NonNull CredentialDataResult.Entries getIssuerSignedEntries() {
+        return mIssuerSignedEntries;
+    }
+
+    static class CredstoreEntries implements CredentialDataResult.Entries {
+        ResultData mResultData;
+
+        CredstoreEntries(ResultData resultData) {
+            mResultData = resultData;
+        }
+
+        @Override
+        public @NonNull Collection<String> getNamespaces() {
+            return mResultData.getNamespaces();
+        }
+
+        @Override
+        public @NonNull Collection<String> getEntryNames(@NonNull String namespaceName) {
+            Collection<String> ret = mResultData.getEntryNames(namespaceName);
+            if (ret == null) {
+                ret = new LinkedList<String>();
+            }
+            return ret;
+        }
+
+        @Override
+        public @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+            Collection<String> ret = mResultData.getRetrievedEntryNames(namespaceName);
+            if (ret == null) {
+                ret = new LinkedList<String>();
+            }
+            return ret;
+        }
+
+        @Override
+        @Status
+        public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+            return mResultData.getStatus(namespaceName, name);
+        }
+
+        @Override
+        public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+            return mResultData.getEntry(namespaceName, name);
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 6398cee..8e01105 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -58,14 +58,17 @@
     private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
     private Context mContext;
     private ICredential mBinder;
+    private CredstorePresentationSession mSession;
 
     CredstoreIdentityCredential(Context context, String credentialName,
             @IdentityCredentialStore.Ciphersuite int cipherSuite,
-            ICredential binder) {
+            ICredential binder,
+            @Nullable CredstorePresentationSession session) {
         mContext = context;
         mCredentialName = credentialName;
         mCipherSuite = cipherSuite;
         mBinder = binder;
+        mSession = session;
     }
 
     private KeyPair mEphemeralKeyPair = null;
@@ -239,6 +242,7 @@
 
     private boolean mAllowUsingExhaustedKeys = true;
     private boolean mAllowUsingExpiredKeys = false;
+    private boolean mIncrementKeyUsageCount = true;
 
     @Override
     public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
@@ -250,6 +254,11 @@
         mAllowUsingExpiredKeys = allowUsingExpiredKeys;
     }
 
+    @Override
+    public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+        mIncrementKeyUsageCount = incrementKeyUsageCount;
+    }
+
     private boolean mOperationHandleSet = false;
     private long mOperationHandle = 0;
 
@@ -264,7 +273,8 @@
         if (!mOperationHandleSet) {
             try {
                 mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
-                        mAllowUsingExpiredKeys);
+                                                         mAllowUsingExpiredKeys,
+                                                         mIncrementKeyUsageCount);
                 mOperationHandleSet = true;
             } catch (android.os.RemoteException e) {
                 throw new RuntimeException("Unexpected RemoteException ", e);
@@ -315,7 +325,8 @@
                 sessionTranscript != null ? sessionTranscript : new byte[0],
                 readerSignature != null ? readerSignature : new byte[0],
                 mAllowUsingExhaustedKeys,
-                mAllowUsingExpiredKeys);
+                mAllowUsingExpiredKeys,
+                mIncrementKeyUsageCount);
         } catch (android.os.RemoteException e) {
             throw new RuntimeException("Unexpected RemoteException ", e);
         } catch (android.os.ServiceSpecificException e) {
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index d8d4742..fb0880c 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -126,7 +126,8 @@
             ICredential credstoreCredential;
             credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
             return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
-                    credstoreCredential);
+                    credstoreCredential,
+                    null);
         } catch (android.os.RemoteException e) {
             throw new RuntimeException("Unexpected RemoteException ", e);
         } catch (android.os.ServiceSpecificException e) {
@@ -162,4 +163,23 @@
                     + e.errorCode, e);
         }
     }
+
+    @Override
+    public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException {
+        try {
+            ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
+            return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
+                throw new CipherSuiteNotSupportedException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
 }
diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java
new file mode 100644
index 0000000..e3c6689
--- /dev/null
+++ b/identity/java/android/security/identity/CredstorePresentationSession.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class CredstorePresentationSession extends PresentationSession {
+    private static final String TAG = "CredstorePresentationSession";
+
+    private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+    private Context mContext;
+    private CredstoreIdentityCredentialStore mStore;
+    private ISession mBinder;
+    private Map<String, CredstoreIdentityCredential> mCredentialCache = new LinkedHashMap<>();
+    private KeyPair mEphemeralKeyPair = null;
+    private byte[] mSessionTranscript = null;
+    private boolean mOperationHandleSet = false;
+    private long mOperationHandle = 0;
+
+    CredstorePresentationSession(Context context,
+            @IdentityCredentialStore.Ciphersuite int cipherSuite,
+            CredstoreIdentityCredentialStore store,
+            ISession binder) {
+        mContext = context;
+        mCipherSuite = cipherSuite;
+        mStore = store;
+        mBinder = binder;
+    }
+
+    private void ensureEphemeralKeyPair() {
+        if (mEphemeralKeyPair != null) {
+            return;
+        }
+        try {
+            // This PKCS#12 blob is generated in credstore, using BoringSSL.
+            //
+            // The main reason for this convoluted approach and not just sending the decomposed
+            // key-pair is that this would require directly using (device-side) BouncyCastle which
+            // is tricky due to various API hiding efforts. So instead we have credstore generate
+            // this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
+            // doesn't support not using encryption when building a PKCS#12 blob).
+            //
+            byte[] pkcs12 = mBinder.getEphemeralKeyPair();
+            String alias = "ephemeralKey";
+            char[] password = {};
+
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
+            ks.load(bais, password);
+            PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
+
+            Certificate cert = ks.getCertificate(alias);
+            PublicKey pubKey = cert.getPublicKey();
+
+            mEphemeralKeyPair = new KeyPair(pubKey, privKey);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        } catch (android.os.RemoteException
+                | KeyStoreException
+                | CertificateException
+                | UnrecoverableKeyException
+                | NoSuchAlgorithmException
+                | IOException e) {
+            throw new RuntimeException("Unexpected exception ", e);
+        }
+    }
+
+    @Override
+    public @NonNull KeyPair getEphemeralKeyPair() {
+        ensureEphemeralKeyPair();
+        return mEphemeralKeyPair;
+    }
+
+    @Override
+    public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException {
+        try {
+            byte[] uncompressedForm =
+                    Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
+            mBinder.setReaderEphemeralPublicKey(uncompressedForm);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public void setSessionTranscript(@NonNull byte[] sessionTranscript) {
+        try {
+            mBinder.setSessionTranscript(sessionTranscript);
+            mSessionTranscript = sessionTranscript;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public @Nullable CredentialDataResult getCredentialData(@NonNull String credentialName,
+                                                            @NonNull CredentialDataRequest request)
+            throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+            InvalidRequestMessageException, EphemeralPublicKeyNotFoundException {
+        try {
+            // Cache the IdentityCredential to satisfy the property that AuthKey usage counts are
+            // incremented on only the _first_ getCredentialData() call.
+            //
+            CredstoreIdentityCredential credential = mCredentialCache.get(credentialName);
+            if (credential == null) {
+                ICredential credstoreCredential =
+                    mBinder.getCredentialForPresentation(credentialName);
+                credential = new CredstoreIdentityCredential(mContext, credentialName,
+                                                             mCipherSuite, credstoreCredential,
+                                                             this);
+                mCredentialCache.put(credentialName, credential);
+
+                credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
+                credential.setAllowUsingExpiredKeys(request.isAllowUsingExpiredKeys());
+                credential.setIncrementKeyUsageCount(request.isIncrementUseCount());
+            }
+
+            ResultData deviceSignedResult = credential.getEntries(
+                    request.getRequestMessage(),
+                    request.getDeviceSignedEntriesToRequest(),
+                    mSessionTranscript,
+                    request.getReaderSignature());
+
+            // By design this second getEntries() call consumes the same auth-key.
+
+            ResultData issuerSignedResult = credential.getEntries(
+                    request.getRequestMessage(),
+                    request.getIssuerSignedEntriesToRequest(),
+                    mSessionTranscript,
+                    request.getReaderSignature());
+
+            return new CredstoreCredentialDataResult(deviceSignedResult, issuerSignedResult);
+
+        } catch (SessionTranscriptMismatchException e) {
+            throw new RuntimeException("Unexpected ", e);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+                return null;
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    @Override
+    public long getCredstoreOperationHandle() {
+        if (!mOperationHandleSet) {
+            try {
+                mOperationHandle = mBinder.getAuthChallenge();
+                mOperationHandleSet = true;
+            } catch (android.os.RemoteException e) {
+                throw new RuntimeException("Unexpected RemoteException ", e);
+            } catch (android.os.ServiceSpecificException e) {
+                if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+                    // The NoAuthenticationKeyAvailableException will be thrown when
+                    // the caller proceeds to call getEntries().
+                }
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+        return mOperationHandle;
+    }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 1e68585..cdf746f 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -48,7 +48,9 @@
      * encryption".
      *
      * @return ephemeral key pair to use to establish a secure channel with a reader.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public @NonNull abstract KeyPair createEphemeralKeyPair();
 
     /**
@@ -58,7 +60,9 @@
      * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
      *                                 establish a secure session.
      * @throws InvalidKeyException if the given key is invalid.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
             throws InvalidKeyException;
 
@@ -72,7 +76,10 @@
      *
      * @param messagePlaintext unencrypted message to encrypt.
      * @return encrypted message.
+     * @deprecated Applications should use {@link PresentationSession} and
+     *             implement encryption/decryption themselves.
      */
+    @Deprecated
     public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
 
     /**
@@ -86,7 +93,10 @@
      * @param messageCiphertext encrypted message to decrypt.
      * @return decrypted message.
      * @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+     * @deprecated Applications should use {@link PresentationSession} and
+     *             implement encryption/decryption themselves.
      */
+    @Deprecated
     public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
             throws MessageDecryptionException;
 
@@ -111,7 +121,9 @@
      *
      * @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
      *                                has been exceeded if no other key is available.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
 
     /**
@@ -128,12 +140,36 @@
      *
      * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
      *                              has been exceeded if no other key is available.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
         throw new UnsupportedOperationException();
     }
 
     /**
+     * @hide
+     *
+     * Sets whether the usage count of an authentication key should be increased. This must be
+     * called prior to calling
+     * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this object.
+     *
+     * <p>By default this is set to true.
+     *
+     * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+     * fails with {@link UnsupportedOperationException}. See
+     * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+     * feature versions.
+     *
+     * @param incrementKeyUsageCount whether the usage count of the key should be increased.
+     * @deprecated Use {@link PresentationSession} instead.
+     */
+    public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
      * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
      * operation handle.
      *
@@ -149,15 +185,19 @@
      * by using the {@link ResultData#getStatus(String, String)} method on each of the requested
      * entries.
      *
-     * <p>It is the responsibility of the calling application to know if authentication is needed
-     * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
-     * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
-     * references this object. If needed, this must be done before calling
-     * {@link #getEntries(byte[], Map, byte[], byte[])}.
-     *
      * <p>It is permissible to call this method multiple times using the same instance but if this
      * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
      * not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
+     * Additionally, if this is done the same auth-key will be used.
+     *
+     * <p>The application should not make any assumptions on whether user authentication is needed.
+     * Instead, the application should request the data elements values first and then examine
+     * the returned {@link ResultData}. If {@link ResultData#STATUS_USER_AUTHENTICATION_FAILED}
+     * is returned the application should get a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+     * authentication the application may call {@link #getEntries(byte[], Map, byte[], byte[])}
+     * again.
      *
      * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
      * from the verifier. The content can be defined in the way appropriate for the credential, but
@@ -269,7 +309,9 @@
      * @throws InvalidRequestMessageException         if the requestMessage is malformed.
      * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
      *                                                the session transcript.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract @NonNull ResultData getEntries(
             @Nullable byte[] requestMessage,
             @NonNull Map<String, Collection<String>> entriesToRequest,
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 6ccd0e8..dbb8aaa 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -209,6 +209,25 @@
     @Deprecated
     public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
 
+    /**
+     * Creates a new presentation session.
+     *
+     * <p>This method gets an object to be used for interaction with a remote verifier for
+     * presentation of one or more credentials.
+     *
+     * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+     * fails with {@link UnsupportedOperationException}. See
+     * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+     * feature versions.
+     *
+     * @param cipherSuite    the cipher suite to use for communicating with the verifier.
+     * @return The presentation session.
+     */
+    public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java
new file mode 100644
index 0000000..afaafce
--- /dev/null
+++ b/identity/java/android/security/identity/PresentationSession.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+/**
+ * Class for presenting multiple documents to a remote verifier.
+ *
+ * Use {@link IdentityCredentialStore#createPresentationSession(int)} to create a {@link
+ * PresentationSession} instance.
+ */
+public abstract class PresentationSession {
+    /**
+     * @hide
+     */
+    protected PresentationSession() {}
+
+    /**
+     * Gets the ephemeral key pair to use to establish a secure channel with the verifier.
+     *
+     * <p>Applications should use this key-pair for the communications channel with the verifier
+     * using a protocol / cipher-suite appropriate for the application. One example of such a
+     * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5.
+     *
+     * <p>The ephemeral key pair is tied to the {@link PresentationSession} instance so subsequent
+     * calls to this method will return the same key-pair.
+     *
+     * @return ephemeral key pair to use to establish a secure channel with a reader.
+     */
+    public @NonNull abstract KeyPair getEphemeralKeyPair();
+
+    /**
+     * Set the ephemeral public key provided by the verifier.
+     *
+     * <p>If called, this must be called before any calls to
+     * {@link #getCredentialData(String, CredentialDataRequest)}.
+     *
+     * <p>This method can only be called once per {@link PresentationSession} instance.
+     *
+     * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
+     *                                 establish a secure session.
+     * @throws InvalidKeyException if the given key is invalid.
+     */
+    public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException;
+
+    /**
+     * Set the session transcript.
+     *
+     * <p>If called, this must be called before any calls to
+     * {@link #getCredentialData(String, CredentialDataRequest)}.
+     *
+     * <p>The X and Y coordinates of the public part of the key-pair returned by {@link
+     * #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR.  Each of
+     * these coordinates must appear encoded with the most significant bits first and use the exact
+     * amount of bits indicated by the key size of the ephemeral keys. For example, if the
+     * ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
+     * the most significant bits first must appear somewhere and ditto for the 32 bytes for the Y
+     * coordinate.
+     *
+     * <p>This method can only be called once per {@link PresentationSession} instance.
+     *
+     * @param sessionTranscript the session transcript.
+     */
+    public abstract void setSessionTranscript(@NonNull byte[] sessionTranscript);
+
+    /**
+     * Retrieves data from a named credential in the current presentation session.
+     *
+     * <p>If an access control check fails for one of the requested entries or if the entry
+     * doesn't exist, the entry is simply not returned. The application can detect this
+     * by using the {@link CredentialDataResult.Entries#getStatus(String, String)} method on
+     * each of the requested entries.
+     *
+     * <p>The application should not make any assumptions on whether user authentication is needed.
+     * Instead, the application should request the data elements values first and then examine
+     * the returned {@link CredentialDataResult.Entries}. If
+     * {@link CredentialDataResult.Entries#STATUS_USER_AUTHENTICATION_FAILED} is returned the
+     * application should get a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+     * authentication the application may call
+     * {@link #getCredentialData(String, CredentialDataRequest)} again.
+     *
+     * <p>It is permissible to call this method multiple times using the same credential name.
+     * If this is done the same auth-key will be used.
+     *
+     * <p>If the reader signature is set in the request parameter (via the
+     * {@link CredentialDataRequest.Builder#setReaderSignature(byte[])} method) it must contain
+     * the bytes of a {@code COSE_Sign1} structure as defined in RFC 8152. For the payload
+     * {@code nil} shall be used and the detached payload is the {@code ReaderAuthenticationBytes}
+     * CBOR described below.
+     * <pre>
+     *     ReaderAuthentication = [
+     *       "ReaderAuthentication",
+     *       SessionTranscript,
+     *       ItemsRequestBytes
+     *     ]
+     *
+     *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+     *
+     *     ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+     * </pre>
+     *
+     * <p>where {@code ItemsRequestBytes} are the bytes of the request message set in
+     * the request parameter (via the
+     * {@link CredentialDataRequest.Builder#setRequestMessage(byte[])} method).
+     *
+     * <p>The public key corresponding to the key used to make the signature, can be found in the
+     * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
+     * described in
+     * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-08">draft-ietf-cose-x509-08</a>).
+     * There will be at least one certificate in said element and there may be more (and if so,
+     * each certificate must be signed by its successor).
+     *
+     * <p>Data elements protected by reader authentication are returned if, and only if,
+     * {@code requestMessage} is signed by the top-most certificate in the reader's certificate
+     * chain, and the data element is configured with an {@link AccessControlProfile} configured
+     * with an X.509 certificate for a key which appear in the certificate chain.
+     *
+     * <p>Note that the request message CBOR is used only for enforcing reader authentication, it's
+     * not used for determining which entries this API will return. The application is expected to
+     * have parsed the request message and filtered it according to user preference and/or consent.
+     *
+     * @param credentialName the name of the credential to retrieve.
+     * @param request the data to retrieve from the credential
+     * @return If the credential wasn't found, returns null. Otherwise a
+     *         {@link CredentialDataResult} object containing entry data organized by namespace and
+     *         a cryptographically authenticated representation of the same data, bound to the
+     *         current session.
+     * @throws NoAuthenticationKeyAvailableException  if authentication keys were never
+     *                                                provisioned for the credential or if they
+     *                                                are expired or exhausted their use-count.
+     * @throws InvalidRequestMessageException         if the requestMessage is malformed.
+     * @throws InvalidReaderSignatureException        if the reader signature is invalid, or it
+     *                                                doesn't contain a certificate chain, or if
+     *                                                the signature failed to validate.
+     * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
+     *                                                the session transcript.
+     */
+    public abstract @Nullable CredentialDataResult getCredentialData(
+            @NonNull String credentialName, @NonNull CredentialDataRequest request)
+            throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+            InvalidRequestMessageException, EphemeralPublicKeyNotFoundException;
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    public abstract long getCredstoreOperationHandle();
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 71860d2..d46f985 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -28,7 +28,10 @@
 /**
  * An object that contains the result of retrieving data from a credential. This is used to return
  * data requested from a {@link IdentityCredential}.
+ *
+ * @deprecated Use {@link PresentationSession} instead.
  */
+@Deprecated
 public abstract class ResultData {
 
     /** Value was successfully retrieved. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 22bec3d..6823639 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1348,7 +1348,7 @@
 
         mStackView.updateContentDescription();
 
-        mStackView.updateBubblesClickableStates();
+        mStackView.updateBubblesAcessibillityStates();
     }
 
     @VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index d5d8b71..a477bd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1486,19 +1486,63 @@
     }
 
     /**
-     * Update bubbles' icon views clickable states.
+     * Update bubbles' icon views accessibility states.
      */
-    public void updateBubblesClickableStates() {
+    public void updateBubblesAcessibillityStates() {
         for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
-            final Bubble bubble = mBubbleData.getBubbles().get(i);
-            if (bubble.getIconView() != null) {
-                if (mIsExpanded) {
-                    // when stack is expanded all bubbles are clickable
-                    bubble.getIconView().setClickable(true);
-                } else {
-                    // when stack is collapsed, only the top bubble needs to be clickable,
-                    // so that a11y ignores all the inaccessible bubbles in the stack
-                    bubble.getIconView().setClickable(i == 0);
+            Bubble prevBubble = i > 0 ? mBubbleData.getBubbles().get(i - 1) : null;
+            Bubble bubble = mBubbleData.getBubbles().get(i);
+
+            View bubbleIconView = bubble.getIconView();
+            if (bubbleIconView == null) {
+                continue;
+            }
+
+            if (mIsExpanded) {
+                // when stack is expanded
+                // all bubbles are important for accessibility
+                bubbleIconView
+                        .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+                View prevBubbleIconView = prevBubble != null ? prevBubble.getIconView() : null;
+
+                if (prevBubbleIconView != null) {
+                    bubbleIconView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+                        @Override
+                        public void onInitializeAccessibilityNodeInfo(View v,
+                                AccessibilityNodeInfo info) {
+                            super.onInitializeAccessibilityNodeInfo(v, info);
+                            info.setTraversalAfter(prevBubbleIconView);
+                        }
+                    });
+                }
+            } else {
+                // when stack is collapsed, only the top bubble is important for accessibility,
+                bubbleIconView.setImportantForAccessibility(
+                        i == 0 ? View.IMPORTANT_FOR_ACCESSIBILITY_YES :
+                                View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+            }
+        }
+
+        if (mIsExpanded) {
+            // make the overflow bubble last in the accessibility traversal order
+
+            View bubbleOverflowIconView =
+                    mBubbleOverflow != null ? mBubbleOverflow.getIconView() : null;
+            if (bubbleOverflowIconView != null && !mBubbleData.getBubbles().isEmpty()) {
+                Bubble lastBubble =
+                        mBubbleData.getBubbles().get(mBubbleData.getBubbles().size() - 1);
+                View lastBubbleIconView = lastBubble.getIconView();
+                if (lastBubbleIconView != null) {
+                    bubbleOverflowIconView.setAccessibilityDelegate(
+                            new View.AccessibilityDelegate() {
+                                @Override
+                                public void onInitializeAccessibilityNodeInfo(View v,
+                                        AccessibilityNodeInfo info) {
+                                    super.onInitializeAccessibilityNodeInfo(v, info);
+                                    info.setTraversalAfter(lastBubbleIconView);
+                                }
+                            });
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 8e6c05d..eda09e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -62,6 +62,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.InstanceId;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -132,6 +133,8 @@
         final Rect fullscreenHitRegion = new Rect(displayRegion);
         final boolean inLandscape = mSession.displayLayout.isLandscape();
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+        final float dividerWidth = mContext.getResources().getDimensionPixelSize(
+                R.dimen.split_divider_bar_width);
         // We allow splitting if we are already in split-screen or the running task is a standard
         // task in fullscreen mode.
         final boolean allowSplit = inSplitScreen
@@ -153,8 +156,11 @@
 
                 // If we have existing split regions use those bounds, otherwise split it 50/50
                 if (inSplitScreen) {
+                    // Add the divider bounds to each side since that counts for the hit region.
                     leftHitRegion.set(topOrLeftBounds);
+                    leftHitRegion.right += dividerWidth / 2;
                     rightHitRegion.set(bottomOrRightBounds);
+                    rightHitRegion.left -= dividerWidth / 2;
                 } else {
                     displayRegion.splitVertically(leftHitRegion, rightHitRegion);
                 }
@@ -170,8 +176,11 @@
 
                 // If we have existing split regions use those bounds, otherwise split it 50/50
                 if (inSplitScreen) {
+                    // Add the divider bounds to each side since that counts for the hit region.
                     topHitRegion.set(topOrLeftBounds);
+                    topHitRegion.bottom += dividerWidth / 2;
                     bottomHitRegion.set(bottomOrRightBounds);
+                    bottomHitRegion.top -= dividerWidth / 2;
                 } else {
                     displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 73f65b0..f8902c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -269,6 +269,8 @@
         // touchable or focusable by the user.  We also add in the ALT_FOCUSABLE_IM
         // flag because we do know that the next window will take input
         // focus, so we want to get the IME window up on top of us right away.
+        // Touches will only pass through to the host activity window and will be blocked from
+        // passing to any other windows.
         windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -276,9 +278,6 @@
         params.token = appToken;
         params.packageName = activityInfo.packageName;
         params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        // Setting as trusted overlay to let touches pass through. This is safe because this
-        // window is controlled by the system.
-        params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
         if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
             params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 11b2387..5054e60 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -291,8 +291,7 @@
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
         };
 
-        boolean requireBackgroundForTransition = false;
-
+        @ColorInt int backgroundColorForTransition = 0;
         final int wallpaperTransit = getWallpaperTransitType(info);
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
@@ -352,8 +351,19 @@
 
             Animation a = loadAnimation(info, change, wallpaperTransit);
             if (a != null) {
-                if (changeRequiresBackground(info, change)) {
-                    requireBackgroundForTransition = true;
+                if (isTask) {
+                    final @TransitionType int type = info.getType();
+                    final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN
+                            || type == TRANSIT_CLOSE
+                            || type == TRANSIT_TO_FRONT
+                            || type == TRANSIT_TO_BACK;
+                    if (isOpenOrCloseTransition) {
+                        // Use the overview background as the background for the animation
+                        final Context uiContext = ActivityThread.currentActivityThread()
+                                .getSystemUiContext();
+                        backgroundColorForTransition =
+                                uiContext.getColor(R.color.overview_background);
+                    }
                 }
 
                 float cornerRadius = 0;
@@ -365,6 +375,15 @@
                             ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
                 }
 
+                if (a.getShowBackground()) {
+                    // use the window's background color if provided as the background color for the
+                    // animation - the top most window with a valid background color and
+                    // showBackground set takes precedence.
+                    if (change.getBackgroundColor() != 0) {
+                        backgroundColorForTransition = change.getBackgroundColor();
+                    }
+                }
+
                 startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
                         mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
                         cornerRadius, change.getEndAbsBounds());
@@ -376,8 +395,9 @@
             }
         }
 
-        if (requireBackgroundForTransition) {
-            addBackgroundToTransition(info.getRootLeash(), startTransaction, finishTransaction);
+        if (backgroundColorForTransition != 0) {
+            addBackgroundToTransition(info.getRootLeash(), backgroundColorForTransition,
+                    startTransaction, finishTransaction);
         }
 
         startTransaction.apply();
@@ -388,24 +408,13 @@
         return true;
     }
 
-    private boolean changeRequiresBackground(TransitionInfo info,
-            TransitionInfo.Change change) {
-        final boolean isTask = change.getTaskInfo() != null;
-        final @TransitionType int type = info.getType();
-        final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN || type == TRANSIT_CLOSE
-                || type == TRANSIT_TO_FRONT || type == TRANSIT_TO_BACK;
-        return isTask && isOpenOrCloseTransition;
-    }
-
     private void addBackgroundToTransition(
             @NonNull SurfaceControl rootLeash,
+            @ColorInt int color,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction
     ) {
-        final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
-        final @ColorInt int overviewBackgroundColor =
-                uiContext.getColor(R.color.overview_background);
-        final Color bgColor = Color.valueOf(overviewBackgroundColor);
+        final Color bgColor = Color.valueOf(color);
         final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
 
         final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 8e6fa5f..7e232ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -66,7 +66,7 @@
         stringExtras: Map<String, String>
     ) {
         super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
-        wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
+        wmHelper.waitPipShown()
     }
 
     private fun focusOnObject(selector: BySelector): Boolean {
@@ -88,7 +88,7 @@
         clickObject(ENTER_PIP_BUTTON_ID)
 
         // Wait on WMHelper or simply wait for 3 seconds
-        wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+        wmHelper?.waitPipShown() ?: SystemClock.sleep(3_000)
         // when entering pip, the dismiss button is visible at the start. to ensure the pip
         // animation is complete, wait until the pip dismiss button is no longer visible. 
         // b/176822698: dismiss-only state will be removed in the future
@@ -148,7 +148,7 @@
         }
 
         // Wait for animation to complete.
-        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+        wmHelper.waitPipGone()
         wmHelper.waitForHomeActivityVisible()
     }
 
@@ -165,7 +165,7 @@
                 ?: error("PIP window expand button not found")
         val expandButtonBounds = expandPipObject.visibleBounds
         uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
-        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+        wmHelper.waitPipGone()
         wmHelper.waitForAppTransitionIdle()
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 2c02d2c..fb1004b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -27,7 +27,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -41,7 +40,6 @@
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -112,11 +110,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 7d7add4..264d482 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -29,7 +29,6 @@
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.resizeSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
@@ -45,7 +44,6 @@
 import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
 import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -137,11 +135,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Test
     fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 5678899..d703ea0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -35,7 +34,6 @@
 import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -79,11 +77,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index c2edf9d..6b18839 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -35,7 +34,6 @@
 import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -78,11 +76,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 777998c..acd658b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -37,7 +36,6 @@
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -88,11 +86,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index 914b11d..b40be8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -37,7 +36,6 @@
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -93,11 +91,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @FlakyTest
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index d3bb008..db94de2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -26,9 +27,7 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -93,11 +92,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     /**
      * Checks [pipApp] window remains visible throughout the animation
@@ -187,13 +182,14 @@
     }
 
     /**
-     * Checks the focus doesn't change during the animation
+     * Checks that the focus changes between the [pipApp] window and the launcher when
+     * closing the pip window
      */
-    @FlakyTest
+    @Postsubmit
     @Test
-    fun focusDoesNotChange() {
+    fun focusChanges() {
         testSpec.assertEventLog {
-            this.focusDoesNotChange()
+            this.focusChanges(pipApp.`package`, "NexusLauncherActivity")
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index fa9fbcd..dee13c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -27,7 +27,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -36,7 +35,6 @@
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -100,7 +98,7 @@
                 // Enter PiP, and assert that the PiP is within bounds now that the device is back
                 // in portrait
                 broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
-                wmHelper.waitFor { it.wmState.hasPipWindow() }
+                wmHelper.waitPipShown()
                 wmHelper.waitForAppTransitionIdle()
                 // during rotation the status bar becomes invisible and reappears at the end
                 wmHelper.waitForNavBarStatusBarVisible()
@@ -121,11 +119,7 @@
      */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     /**
      * Checks that all parts of the screen are covered at the start and end of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index f8a3aff..990872f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,9 +18,7 @@
 
 import android.platform.test.annotations.Presubmit
 import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import org.junit.Assume.assumeFalse
 import org.junit.Test
 
 /**
@@ -29,15 +27,6 @@
 abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     protected val testApp = FixedAppHelper(instrumentation)
 
-    /** {@inheritDoc}  */
-    @Presubmit
-    @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
-
     /**
      * Checks that the pip app window remains inside the display bounds throughout the whole
      * animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 9a22007..173140d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -16,9 +16,9 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -96,14 +96,13 @@
     }
 
     /**
-     * Checks that the focus changes between the [pipApp] window and the launcher when
-     * closing the pip window
+     * Checks that the focus doesn't change between windows during the transition
      */
-    @FlakyTest(bugId = 151179149)
+    @Postsubmit
     @Test
-    open fun focusChanges() {
+    open fun focusDoesNotChange() {
         testSpec.assertEventLog {
-            this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+            this.focusDoesNotChange()
         }
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 2231d88..2c08b7f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -24,8 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -82,11 +80,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index fcac2c7..e340f4c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -24,9 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -101,11 +99,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 8e6603b..6524182 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -24,9 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -65,7 +63,7 @@
                 val pipCenterY = pipRegion.centerY()
                 val displayCenterX = device.displayWidth / 2
                 device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10)
-                wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+                wmHelper.waitPipGone()
                 wmHelper.waitForWindowSurfaceDisappeared(pipApp.component)
                 wmHelper.waitForAppTransitionIdle()
             }
@@ -81,11 +79,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index ef9ff4f..8d14f70 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -148,7 +149,7 @@
     /**
      * Checks that the focus doesn't change between windows during the transition
      */
-    @FlakyTest
+    @Postsubmit
     @Test
     fun focusDoesNotChange() {
         testSpec.assertEventLog {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index f9e180e..e415024 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -24,8 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
 import com.android.server.wm.flicker.traces.region.RegionSubject
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -85,11 +83,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index b7bfa1b..4a4c46c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -25,8 +25,6 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -85,11 +83,7 @@
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 93a4e1b..bb66f7b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -125,13 +125,13 @@
                     removeAllTasksButHome()
                     if (!eachRun) {
                         pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
-                        wmHelper.waitFor { it.wmState.hasPipWindow() }
+                        wmHelper.waitPipShown()
                     }
                 }
                 eachRun {
                     if (eachRun) {
                         pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
-                        wmHelper.waitFor { it.wmState.hasPipWindow() }
+                        wmHelper.waitPipShown()
                     }
                 }
             }
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index c4366f75..c505b53 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -64,7 +64,8 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
-        jint tileModeX, jint tileModeY, bool filter) {
+                                      jint tileModeX, jint tileModeY, bool filter,
+                                      bool isDirectSampled) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     sk_sp<SkImage> image;
     if (bitmapHandle) {
@@ -79,8 +80,12 @@
     }
     SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
                                SkMipmapMode::kNone);
-    sk_sp<SkShader> shader = image->makeShader(
-            (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    sk_sp<SkShader> shader;
+    if (isDirectSampled) {
+        shader = image->makeRawShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    } else {
+        shader = image->makeShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    }
     ThrowIAE_IfNull(env, shader.get());
 
     if (matrix) {
@@ -393,7 +398,7 @@
 };
 
 static const JNINativeMethod gBitmapShaderMethods[] = {
-    { "nativeCreate",      "(JJIIZ)J",  (void*)BitmapShader_constructor },
+        {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor},
 };
 
 static const JNINativeMethod gLinearGradientMethods[] = {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 6755b7c..d862377 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -119,7 +119,7 @@
             AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(cbData);
     int64_t vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
             cbData, preferredFrameTimelineIndex);
-    int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+    int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
             cbData, preferredFrameTimelineIndex);
     int64_t frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(cbData);
     // TODO(b/193273294): Remove when shared memory in use w/ expected present time always current.
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 55f932d..6c0fd5f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -55,6 +55,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wthread-safety",
     ],
 
 }
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index f43586f..1dc74e5 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -18,11 +18,13 @@
 //#define LOG_NDEBUG 0
 
 #include "PointerController.h"
-#include "PointerControllerContext.h"
 
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkColor.h>
+#include <android-base/thread_annotations.h>
+
+#include "PointerControllerContext.h"
 
 namespace android {
 
@@ -36,8 +38,18 @@
 
 void PointerController::DisplayInfoListener::onWindowInfosChanged(
         const std::vector<android::gui::WindowInfo>&,
-        const std::vector<android::gui::DisplayInfo>& displayInfo) {
-    mPointerController.onDisplayInfosChanged(displayInfo);
+        const std::vector<android::gui::DisplayInfo>& displayInfos) {
+    std::scoped_lock lock(mLock);
+    if (mPointerController == nullptr) return;
+
+    // PointerController uses DisplayInfoListener's lock.
+    base::ScopedLockAssertion assumeLocked(mPointerController->getLock());
+    mPointerController->onDisplayInfosChangedLocked(displayInfos);
+}
+
+void PointerController::DisplayInfoListener::onPointerControllerDestroyed() {
+    std::scoped_lock lock(mLock);
+    mPointerController = nullptr;
 }
 
 // --- PointerController ---
@@ -68,16 +80,36 @@
 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
                                      const sp<Looper>& looper,
                                      const sp<SpriteController>& spriteController)
+      : PointerController(
+                policy, looper, spriteController,
+                [](const sp<android::gui::WindowInfosListener>& listener) {
+                    SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
+                },
+                [](const sp<android::gui::WindowInfosListener>& listener) {
+                    SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+                }) {}
+
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+                                     const sp<Looper>& looper,
+                                     const sp<SpriteController>& spriteController,
+                                     WindowListenerConsumer registerListener,
+                                     WindowListenerConsumer unregisterListener)
       : mContext(policy, looper, spriteController, *this),
         mCursorController(mContext),
-        mDisplayInfoListener(new DisplayInfoListener(*this)) {
-    std::scoped_lock lock(mLock);
+        mDisplayInfoListener(new DisplayInfoListener(this)),
+        mUnregisterWindowInfosListener(std::move(unregisterListener)) {
+    std::scoped_lock lock(getLock());
     mLocked.presentation = Presentation::SPOT;
-    SurfaceComposerClient::getDefault()->addWindowInfosListener(mDisplayInfoListener);
+    registerListener(mDisplayInfoListener);
 }
 
 PointerController::~PointerController() {
-    SurfaceComposerClient::getDefault()->removeWindowInfosListener(mDisplayInfoListener);
+    mDisplayInfoListener->onPointerControllerDestroyed();
+    mUnregisterWindowInfosListener(mDisplayInfoListener);
+}
+
+std::mutex& PointerController::getLock() const {
+    return mDisplayInfoListener->mLock;
 }
 
 bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -89,7 +121,7 @@
     const int32_t displayId = mCursorController.getDisplayId();
     vec2 transformed;
     {
-        std::scoped_lock lock(mLock);
+        std::scoped_lock lock(getLock());
         const auto& transform = getTransformForDisplayLocked(displayId);
         transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
     }
@@ -108,7 +140,7 @@
     const int32_t displayId = mCursorController.getDisplayId();
     vec2 transformed;
     {
-        std::scoped_lock lock(mLock);
+        std::scoped_lock lock(getLock());
         const auto& transform = getTransformForDisplayLocked(displayId);
         transformed = transform.transform(x, y);
     }
@@ -119,7 +151,7 @@
     const int32_t displayId = mCursorController.getDisplayId();
     mCursorController.getPosition(outX, outY);
     {
-        std::scoped_lock lock(mLock);
+        std::scoped_lock lock(getLock());
         const auto& transform = getTransformForDisplayLocked(displayId);
         const auto xy = transform.inverse().transform(*outX, *outY);
         *outX = xy.x;
@@ -132,17 +164,17 @@
 }
 
 void PointerController::fade(Transition transition) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.fade(transition);
 }
 
 void PointerController::unfade(Transition transition) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.unfade(transition);
 }
 
 void PointerController::setPresentation(Presentation presentation) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
 
     if (mLocked.presentation == presentation) {
         return;
@@ -162,7 +194,7 @@
 
 void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
                                  BitSet32 spotIdBits, int32_t displayId) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
     const ui::Transform& transform = getTransformForDisplayLocked(displayId);
 
@@ -185,11 +217,11 @@
 }
 
 void PointerController::clearSpots() {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     clearSpotsLocked();
 }
 
-void PointerController::clearSpotsLocked() REQUIRES(mLock) {
+void PointerController::clearSpotsLocked() {
     for (auto& [displayID, spotController] : mLocked.spotControllers) {
         spotController.clearSpots();
     }
@@ -200,7 +232,7 @@
 }
 
 void PointerController::reloadPointerResources() {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
 
     for (auto& [displayID, spotController] : mLocked.spotControllers) {
         spotController.reloadSpotResources();
@@ -216,7 +248,7 @@
 }
 
 void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
 
     bool getAdditionalMouseResources = false;
     if (mLocked.presentation == PointerController::Presentation::POINTER) {
@@ -226,12 +258,12 @@
 }
 
 void PointerController::updatePointerIcon(int32_t iconId) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.updatePointerIcon(iconId);
 }
 
 void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCursorController.setCustomPointerIcon(icon);
 }
 
@@ -245,7 +277,7 @@
         displayIdSet.insert(viewport.displayId);
     }
 
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
         int32_t displayID = it->first;
         if (!displayIdSet.count(displayID)) {
@@ -261,8 +293,8 @@
     }
 }
 
-void PointerController::onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfo) {
-    std::scoped_lock lock(mLock);
+void PointerController::onDisplayInfosChangedLocked(
+        const std::vector<gui::DisplayInfo>& displayInfo) {
     mLocked.mDisplayInfos = displayInfo;
 }
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 796077f..2e6e851 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -72,13 +72,31 @@
     void reloadPointerResources();
     void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
 
-    void onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfos);
+    void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+            REQUIRES(getLock());
+
+protected:
+    using WindowListenerConsumer =
+            std::function<void(const sp<android::gui::WindowInfosListener>&)>;
+
+    // Constructor used to test WindowInfosListener registration.
+    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+                      const sp<SpriteController>& spriteController,
+                      WindowListenerConsumer registerListener,
+                      WindowListenerConsumer unregisterListener);
 
 private:
+    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+                      const sp<SpriteController>& spriteController);
+
     friend PointerControllerContext::LooperCallback;
     friend PointerControllerContext::MessageHandler;
 
-    mutable std::mutex mLock;
+    // PointerController's DisplayInfoListener can outlive the PointerController because when the
+    // listener is registered, a strong pointer to the listener (which can extend its lifecycle)
+    // is given away. To avoid the small overhead of using two separate locks in these two objects,
+    // we use the DisplayInfoListener's lock in PointerController.
+    std::mutex& getLock() const;
 
     PointerControllerContext mContext;
 
@@ -89,24 +107,28 @@
 
         std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
-    } mLocked GUARDED_BY(mLock);
+    } mLocked GUARDED_BY(getLock());
 
     class DisplayInfoListener : public gui::WindowInfosListener {
     public:
-        explicit DisplayInfoListener(PointerController& pc) : mPointerController(pc){};
+        explicit DisplayInfoListener(PointerController* pc) : mPointerController(pc){};
         void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
                                   const std::vector<android::gui::DisplayInfo>&) override;
+        void onPointerControllerDestroyed();
+
+        // This lock is also used by PointerController. See PointerController::getLock().
+        std::mutex mLock;
 
     private:
-        PointerController& mPointerController;
+        PointerController* mPointerController GUARDED_BY(mLock);
     };
+
     sp<DisplayInfoListener> mDisplayInfoListener;
+    const WindowListenerConsumer mUnregisterWindowInfosListener;
 
-    const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(mLock);
+    const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(getLock());
 
-    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
-                      const sp<SpriteController>& spriteController);
-    void clearSpotsLocked();
+    void clearSpotsLocked() REQUIRES(getLock());
 };
 
 } // namespace android
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index b67088a..dae1fcc 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -255,4 +255,36 @@
     ensureDisplayViewportIsSet();
 }
 
+class PointerControllerWindowInfoListenerTest : public Test {};
+
+class TestPointerController : public PointerController {
+public:
+    TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
+                          const sp<Looper>& looper)
+          : PointerController(
+                    new MockPointerControllerPolicyInterface(), looper,
+                    new NiceMock<MockSpriteController>(looper),
+                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+                        // Register listener
+                        registeredListener = listener;
+                    },
+                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+                        // Unregister listener
+                        if (registeredListener == listener) registeredListener = nullptr;
+                    }) {}
+};
+
+TEST_F(PointerControllerWindowInfoListenerTest,
+       doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
+    sp<android::gui::WindowInfosListener> registeredListener;
+    sp<android::gui::WindowInfosListener> localListenerCopy;
+    {
+        TestPointerController pointerController(registeredListener, new Looper(false));
+        ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
+        localListenerCopy = registeredListener;
+    }
+    EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
+    localListenerCopy->onWindowInfosChanged({}, {});
+}
+
 }  // namespace android
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index e4a0d0c..a158344 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -298,6 +298,7 @@
      * @param lowerLeftLongitude  the longitude of the lower left corner of the bounding box
      * @param upperRightLatitude  the latitude of the upper right corner of the bounding box
      * @param upperRightLongitude the longitude of the upper right corner of the bounding box
+     * @param listener            a listener for receiving results
      *
      * @throws IllegalArgumentException if locationName is null
      * @throws IllegalArgumentException if any latitude or longitude is invalid
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/location/java/android/location/GnssAutomaticGainControl.aidl
similarity index 82%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to location/java/android/location/GnssAutomaticGainControl.aidl
index 6041460..8298cb71 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/location/java/android/location/GnssAutomaticGainControl.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package android.location;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable GnssAutomaticGainControl;
diff --git a/location/java/android/location/GnssAutomaticGainControl.java b/location/java/android/location/GnssAutomaticGainControl.java
new file mode 100644
index 0000000..e4f7304
--- /dev/null
+++ b/location/java/android/location/GnssAutomaticGainControl.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A class that contains GNSS Automatic Gain Control (AGC) information.
+ *
+ * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
+ * level may be used to indicate potential interference. Higher gain (and/or lower input power)
+ * shall be output as a positive number. Hence in cases of strong jamming, in the band of this
+ * signal, this value will go more negative. This value must be consistent given the same level
+ * of the incoming signal power.
+ *
+ * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * components) may also affect the typical output of this value on any given hardware design
+ * in an open sky test - the important aspect of this output is that changes in this value are
+ * indicative of changes on input signal power in the frequency band for this measurement.
+ */
+public final class GnssAutomaticGainControl implements Parcelable {
+    private final double mLevelDb;
+    private final int mConstellationType;
+    private final long mCarrierFrequencyHz;
+
+    /**
+     * Creates a {@link GnssAutomaticGainControl} with a full list of parameters.
+     */
+    private GnssAutomaticGainControl(double levelDb, int constellationType,
+            long carrierFrequencyHz) {
+        mLevelDb = levelDb;
+        mConstellationType = constellationType;
+        mCarrierFrequencyHz = carrierFrequencyHz;
+    }
+
+    /**
+     * Gets the Automatic Gain Control level in dB.
+     */
+    @FloatRange(from = -10000, to = 10000)
+    public double getLevelDb() {
+        return mLevelDb;
+    }
+
+    /**
+     * Gets the constellation type.
+     *
+     * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+     * {@link GnssStatus}.
+     */
+    @GnssStatus.ConstellationType
+    public int getConstellationType() {
+        return mConstellationType;
+    }
+
+    /**
+     * Gets the carrier frequency of the tracked signal.
+     *
+     * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+     * L5 = 1176.45 MHz, varying GLO channels, etc.
+     *
+     * @return the carrier frequency of the signal tracked in Hz.
+     */
+    @IntRange(from = 0)
+    public long getCarrierFrequencyHz() {
+        return mCarrierFrequencyHz;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flag) {
+        parcel.writeDouble(mLevelDb);
+        parcel.writeInt(mConstellationType);
+        parcel.writeLong(mCarrierFrequencyHz);
+    }
+
+    @NonNull
+    public static final Creator<GnssAutomaticGainControl> CREATOR =
+            new Creator<GnssAutomaticGainControl>() {
+                @Override
+                @NonNull
+                public GnssAutomaticGainControl createFromParcel(@NonNull Parcel parcel) {
+                    return new GnssAutomaticGainControl(parcel.readDouble(), parcel.readInt(),
+                            parcel.readLong());
+                }
+
+                @Override
+                public GnssAutomaticGainControl[] newArray(int i) {
+                    return new GnssAutomaticGainControl[i];
+                }
+            };
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("GnssAutomaticGainControl[");
+        s.append("Level=").append(mLevelDb).append(" dB");
+        s.append(" Constellation=").append(
+                GnssStatus.constellationTypeToString(mConstellationType));
+        s.append(" CarrierFrequency=").append(mCarrierFrequencyHz).append(" Hz");
+        s.append(']');
+        return s.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof GnssAutomaticGainControl)) {
+            return false;
+        }
+
+        GnssAutomaticGainControl other = (GnssAutomaticGainControl) obj;
+        if (Double.compare(mLevelDb, other.mLevelDb)
+                != 0) {
+            return false;
+        }
+        if (mConstellationType != other.mConstellationType) {
+            return false;
+        }
+        if (mCarrierFrequencyHz != other.mCarrierFrequencyHz) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLevelDb, mConstellationType, mCarrierFrequencyHz);
+    }
+
+    /** Builder for {@link GnssAutomaticGainControl} */
+    public static final class Builder {
+        private double mLevelDb;
+        private int mConstellationType;
+        private long mCarrierFrequencyHz;
+
+        /**
+         * Constructs a {@link GnssAutomaticGainControl.Builder} instance.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a {@link GnssAutomaticGainControl.Builder} instance by copying a
+         * {@link GnssAutomaticGainControl}.
+         */
+        public Builder(@NonNull GnssAutomaticGainControl agc) {
+            mLevelDb = agc.getLevelDb();
+            mConstellationType = agc.getConstellationType();
+            mCarrierFrequencyHz = agc.getCarrierFrequencyHz();
+        }
+
+        /**
+         * Sets the Automatic Gain Control level in dB.
+         */
+        @NonNull
+        public Builder setLevelDb(@FloatRange(from = -10000, to = 10000) double levelDb) {
+            Preconditions.checkArgument(levelDb >= -10000 && levelDb <= 10000);
+            mLevelDb = levelDb;
+            return this;
+        }
+
+        /**
+         * Sets the constellation type.
+         */
+        @NonNull
+        public Builder setConstellationType(@GnssStatus.ConstellationType int constellationType) {
+            mConstellationType = constellationType;
+            return this;
+        }
+
+        /**
+         * Sets the Carrier frequency in Hz.
+         */
+        @NonNull public Builder setCarrierFrequencyHz(@IntRange(from = 0) long carrierFrequencyHz) {
+            Preconditions.checkArgumentNonnegative(carrierFrequencyHz);
+            mCarrierFrequencyHz = carrierFrequencyHz;
+            return this;
+        }
+
+        /** Builds a {@link GnssAutomaticGainControl} instance as specified by this builder. */
+        @NonNull
+        public GnssAutomaticGainControl build() {
+            return new GnssAutomaticGainControl(mLevelDb, mConstellationType, mCarrierFrequencyHz);
+        }
+    }
+}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index ecdd4b6..cdfa02c 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1381,7 +1381,10 @@
     /**
      * Returns {@code true} if {@link #getAutomaticGainControlLevelDb()} is available,
      * {@code false} otherwise.
+     *
+     * @deprecated Use {@link GnssMeasurementsEvent#getGnssAutomaticGainControls()} instead.
      */
+    @Deprecated
     public boolean hasAutomaticGainControlLevelDb() {
         return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
     }
@@ -1401,7 +1404,10 @@
      * indicative of changes on input signal power in the frequency band for this measurement.
      *
      * <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+     *
+     * @deprecated Use {@link GnssMeasurementsEvent#getGnssAutomaticGainControls()} instead.
      */
+    @Deprecated
     public double getAutomaticGainControlLevelDb() {
         return mAutomaticGainControlLevelInDb;
     }
@@ -1409,7 +1415,9 @@
     /**
      * Sets the Automatic Gain Control level in dB.
      * @hide
+     * @deprecated Use {@link GnssMeasurementsEvent.Builder#setGnssAutomaticGainControls()} instead.
      */
+    @Deprecated
     @TestApi
     public void setAutomaticGainControlLevelInDb(double agcLevelDb) {
         setFlag(HAS_AUTOMATIC_GAIN_CONTROL);
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index a07a64a..075ddeb 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -18,16 +18,19 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * A class implementing a container for data associated with a measurement event.
@@ -35,7 +38,8 @@
  */
 public final class GnssMeasurementsEvent implements Parcelable {
     private final GnssClock mClock;
-    private final Collection<GnssMeasurement> mReadOnlyMeasurements;
+    private final List<GnssMeasurement> mMeasurements;
+    private final List<GnssAutomaticGainControl> mGnssAgcs;
 
     /**
      * Used for receiving GNSS satellite measurements from the GNSS engine.
@@ -116,20 +120,13 @@
     }
 
     /**
-     * @hide
+     * Create a {@link GnssMeasurementsEvent} instance with a full list of parameters.
      */
-    @TestApi
-    public GnssMeasurementsEvent(GnssClock clock, GnssMeasurement[] measurements) {
-        if (clock == null) {
-            throw new InvalidParameterException("Parameter 'clock' must not be null.");
-        }
-        if (measurements == null || measurements.length == 0) {
-            mReadOnlyMeasurements = Collections.emptyList();
-        } else {
-            Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
-            mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
-        }
-
+    private GnssMeasurementsEvent(@NonNull GnssClock clock,
+            @NonNull List<GnssMeasurement> measurements,
+            @NonNull List<GnssAutomaticGainControl> agcs) {
+        mMeasurements = measurements;
+        mGnssAgcs = agcs;
         mClock = clock;
     }
 
@@ -143,26 +140,31 @@
     }
 
     /**
-     * Gets a read-only collection of measurements associated with the current event.
+     * Gets the collection of measurements associated with the current event.
      */
     @NonNull
     public Collection<GnssMeasurement> getMeasurements() {
-        return mReadOnlyMeasurements;
+        return mMeasurements;
+    }
+
+    /**
+     * Gets the collection of {@link GnssAutomaticGainControl} associated with the
+     * current event.
+     */
+    @NonNull
+    public Collection<GnssAutomaticGainControl> getGnssAutomaticGainControls() {
+        return mGnssAgcs;
     }
 
     public static final @android.annotation.NonNull Creator<GnssMeasurementsEvent> CREATOR =
             new Creator<GnssMeasurementsEvent>() {
         @Override
         public GnssMeasurementsEvent createFromParcel(Parcel in) {
-            ClassLoader classLoader = getClass().getClassLoader();
-
-            GnssClock clock = in.readParcelable(classLoader);
-
-            int measurementsLength = in.readInt();
-            GnssMeasurement[] measurementsArray = new GnssMeasurement[measurementsLength];
-            in.readTypedArray(measurementsArray, GnssMeasurement.CREATOR);
-
-            return new GnssMeasurementsEvent(clock, measurementsArray);
+            GnssClock clock = in.readParcelable(getClass().getClassLoader());
+            List<GnssMeasurement> measurements = in.createTypedArrayList(GnssMeasurement.CREATOR);
+            List<GnssAutomaticGainControl> agcs = in.createTypedArrayList(
+                    GnssAutomaticGainControl.CREATOR);
+            return new GnssMeasurementsEvent(clock, measurements, agcs);
         }
 
         @Override
@@ -179,28 +181,105 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeParcelable(mClock, flags);
-
-        int measurementsCount = mReadOnlyMeasurements.size();
-        GnssMeasurement[] measurementsArray =
-                mReadOnlyMeasurements.toArray(new GnssMeasurement[measurementsCount]);
-        parcel.writeInt(measurementsArray.length);
-        parcel.writeTypedArray(measurementsArray, flags);
+        parcel.writeTypedList(mMeasurements);
+        parcel.writeTypedList(mGnssAgcs);
     }
 
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder("[ GnssMeasurementsEvent:\n\n");
+        StringBuilder builder = new StringBuilder("GnssMeasurementsEvent[");
+        builder.append(mClock);
+        builder.append(' ').append(mMeasurements.toString());
+        builder.append(' ').append(mGnssAgcs.toString());
+        builder.append("]");
+        return builder.toString();
+    }
 
-        builder.append(mClock.toString());
-        builder.append("\n");
+    /** Builder for {@link GnssMeasurementsEvent} */
+    public static final class Builder {
+        private GnssClock mClock;
+        private List<GnssMeasurement> mMeasurements;
+        private List<GnssAutomaticGainControl> mGnssAgcs;
 
-        for (GnssMeasurement measurement : mReadOnlyMeasurements) {
-            builder.append(measurement.toString());
-            builder.append("\n");
+        /**
+         * Constructs a {@link GnssMeasurementsEvent.Builder} instance.
+         */
+        public Builder() {
+            mClock = new GnssClock();
+            mMeasurements = new ArrayList<>();
+            mGnssAgcs = new ArrayList<>();
         }
 
-        builder.append("]");
+        /**
+         * Constructs a {@link GnssMeasurementsEvent.Builder} instance by copying a
+         * {@link GnssMeasurementsEvent}.
+         */
+        public Builder(@NonNull GnssMeasurementsEvent event) {
+            mClock = event.getClock();
+            mMeasurements = (List<GnssMeasurement>) event.getMeasurements();
+            mGnssAgcs = (List<GnssAutomaticGainControl>) event.getGnssAutomaticGainControls();
+        }
 
-        return builder.toString();
+        /**
+         * Sets the {@link GnssClock}.
+         */
+        @NonNull
+        public Builder setClock(@NonNull GnssClock clock) {
+            Preconditions.checkNotNull(clock);
+            mClock = clock;
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssMeasurement}.
+         *
+         * This API exists for JNI since it is easier for JNI to work with an array than a
+         * collection.
+         * @hide
+         */
+        @NonNull
+        public Builder setMeasurements(@Nullable GnssMeasurement... measurements) {
+            mMeasurements = measurements == null ? Collections.emptyList() : Arrays.asList(
+                    measurements);
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssMeasurement}.
+         */
+        @NonNull
+        public Builder setMeasurements(@NonNull Collection<GnssMeasurement> measurements) {
+            mMeasurements = new ArrayList<>(measurements);
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssAutomaticGainControl}.
+         *
+         * This API exists for JNI since it is easier for JNI to work with an array than a
+         * collection.
+         * @hide
+         */
+        @NonNull
+        public Builder setGnssAutomaticGainControls(@Nullable GnssAutomaticGainControl... agcs) {
+            mGnssAgcs = agcs == null ? Collections.emptyList() : Arrays.asList(agcs);
+            return this;
+        }
+
+        /**
+         * Sets the collection of {@link GnssAutomaticGainControl}.
+         */
+        @NonNull
+        public Builder setGnssAutomaticGainControls(
+                @NonNull Collection<GnssAutomaticGainControl> agcs) {
+            mGnssAgcs = new ArrayList<>(agcs);
+            return this;
+        }
+
+        /** Builds a {@link GnssMeasurementsEvent} instance as specified by this builder. */
+        @NonNull
+        public GnssMeasurementsEvent build() {
+            return new GnssMeasurementsEvent(mClock, mMeasurements, mGnssAgcs);
+        }
     }
 }
diff --git a/media/java/Android.bp b/media/java/Android.bp
index eeaf6e9..c7c1d54 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -8,7 +8,7 @@
 }
 
 filegroup {
-    name: "framework-media-sources",
+    name: "framework-media-non-updatable-sources",
     srcs: [
         "**/*.java",
         "**/*.aidl",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 21fc6ec..68e5d94 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -79,6 +79,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -2995,19 +2996,17 @@
         void onModeChanged(@AudioMode int mode);
     }
 
-    private final Object mModeListenerLock = new Object();
     /**
-     * List of listeners for audio mode and their associated Executor.
-     * List is lazy-initialized on first registration
+     * manages the OnModeChangedListener listeners and the ModeDispatcherStub
      */
-    @GuardedBy("mModeListenerLock")
-    private @Nullable ArrayList<ListenerInfo<OnModeChangedListener>> mModeListeners;
+    private final CallbackUtil.LazyListenerManager<OnModeChangedListener> mModeChangedListenerMgr =
+            new CallbackUtil.LazyListenerManager();
 
-    @GuardedBy("mModeListenerLock")
-    private ModeDispatcherStub mModeDispatcherStub;
 
-    private final class ModeDispatcherStub extends IAudioModeDispatcher.Stub {
+    final class ModeDispatcherStub extends IAudioModeDispatcher.Stub
+            implements CallbackUtil.DispatcherStub {
 
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -3021,10 +3020,8 @@
         }
 
         @Override
-        @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchAudioModeChanged(int mode) {
-            CallbackUtil.callListeners(mModeListeners, mModeListenerLock,
-                    (listener) -> listener.onModeChanged(mode));
+            mModeChangedListenerMgr.callListeners((listener) -> listener.onModeChanged(mode));
         }
     }
 
@@ -3037,15 +3034,8 @@
     public void addOnModeChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnModeChangedListener listener) {
-        synchronized (mModeListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res =
-                    CallbackUtil.addListener("addOnModeChangedListener",
-                            executor, listener, mModeListeners, mModeDispatcherStub,
-                            () -> new ModeDispatcherStub(),
-                            stub -> stub.register(true));
-            mModeListeners = res.first;
-            mModeDispatcherStub = res.second;
-        }
+        mModeChangedListenerMgr.addListener(executor, listener, "addOnModeChangedListener",
+                () -> new ModeDispatcherStub());
     }
 
     /**
@@ -3054,14 +3044,7 @@
      * @param listener
      */
     public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) {
-        synchronized (mModeListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnModeChangedListener>>, ModeDispatcherStub> res =
-                    CallbackUtil.removeListener("removeOnModeChangedListener",
-                            listener, mModeListeners, mModeDispatcherStub,
-                            stub -> stub.register(false));
-            mModeListeners = res.first;
-            mModeDispatcherStub = res.second;
-        }
+        mModeChangedListenerMgr.removeListener(listener, "removeOnModeChangedListener");
     }
 
     /**
@@ -5666,6 +5649,43 @@
     }
 
     /**
+     * Get the audio devices that would be used for the routing of the given audio attributes.
+     * These are the devices anticipated to play sound from an {@link AudioTrack} created with
+     * the specified {@link AudioAttributes}.
+     * The audio routing can change if audio devices are physically connected or disconnected or
+     * concurrently through {@link AudioRouting} or {@link MediaRouter}.
+     * @param attributes the {@link AudioAttributes} for which the routing is being queried
+     * @return an empty list if there was an issue with the request, a list of
+     * {@link AudioDeviceInfo} otherwise (typically one device, except for duplicated paths).
+     */
+    public @NonNull List<AudioDeviceInfo> getAudioDevicesForAttributes(
+            @NonNull AudioAttributes attributes) {
+        final List<AudioDeviceAttributes> devicesForAttributes;
+        try {
+            Objects.requireNonNull(attributes);
+            final IAudioService service = getService();
+            devicesForAttributes = service.getDevicesForAttributesUnprotected(attributes);
+        } catch (Exception e) {
+            Log.i(TAG, "No audio devices available for specified attributes.");
+            return Collections.emptyList();
+        }
+
+        // Map from AudioDeviceAttributes to AudioDeviceInfo
+        AudioDeviceInfo[] outputDeviceInfos = getDevicesStatic(GET_DEVICES_OUTPUTS);
+        List<AudioDeviceInfo> deviceInfosForAttributes = new ArrayList<>();
+        for (AudioDeviceAttributes deviceForAttributes : devicesForAttributes) {
+            for (AudioDeviceInfo deviceInfo : outputDeviceInfos) {
+                if (deviceForAttributes.getType() == deviceInfo.getType()
+                        && TextUtils.equals(deviceForAttributes.getAddress(),
+                                deviceInfo.getAddress())) {
+                    deviceInfosForAttributes.add(deviceInfo);
+                }
+            }
+        }
+        return Collections.unmodifiableList(deviceInfosForAttributes);
+    }
+
+    /**
      * @hide
      * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
      * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
@@ -7718,6 +7738,12 @@
     }
 
     /**
+     * manages the OnCommunicationDeviceChangedListener listeners and the
+     * CommunicationDeviceDispatcherStub
+     */
+    private final CallbackUtil.LazyListenerManager<OnCommunicationDeviceChangedListener>
+            mCommDeviceChangedListenerMgr = new CallbackUtil.LazyListenerManager();
+    /**
      * Adds a listener for being notified of changes to the communication audio device.
      * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
      * @param executor
@@ -7726,16 +7752,9 @@
     public void addOnCommunicationDeviceChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnCommunicationDeviceChangedListener listener) {
-        synchronized (mCommDevListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>,
-                    CommunicationDeviceDispatcherStub> res =
-                    CallbackUtil.addListener("addOnCommunicationDeviceChangedListener",
-                            executor, listener, mCommDevListeners, mCommDevDispatcherStub,
-                            () -> new CommunicationDeviceDispatcherStub(),
-                            stub -> stub.register(true));
-            mCommDevListeners = res.first;
-            mCommDevDispatcherStub = res.second;
-        }
+        mCommDeviceChangedListenerMgr.addListener(
+                            executor, listener, "addOnCommunicationDeviceChangedListener",
+                            () -> new CommunicationDeviceDispatcherStub());
     }
 
     /**
@@ -7745,32 +7764,14 @@
      */
     public void removeOnCommunicationDeviceChangedListener(
             @NonNull OnCommunicationDeviceChangedListener listener) {
-        synchronized (mCommDevListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>>,
-                    CommunicationDeviceDispatcherStub> res =
-                    CallbackUtil.removeListener("removeOnCommunicationDeviceChangedListener",
-                            listener, mCommDevListeners, mCommDevDispatcherStub,
-                            stub -> stub.register(false));
-            mCommDevListeners = res.first;
-            mCommDevDispatcherStub = res.second;
-        }
+        mCommDeviceChangedListenerMgr.removeListener(listener,
+                "removeOnCommunicationDeviceChangedListener");
     }
 
-    private final Object mCommDevListenerLock = new Object();
-    /**
-     * List of listeners for preferred device for strategy and their associated Executor.
-     * List is lazy-initialized on first registration
-     */
-    @GuardedBy("mCommDevListenerLock")
-    private @Nullable
-            ArrayList<ListenerInfo<OnCommunicationDeviceChangedListener>> mCommDevListeners;
-
-    @GuardedBy("mCommDevListenerLock")
-    private CommunicationDeviceDispatcherStub mCommDevDispatcherStub;
-
     private final class CommunicationDeviceDispatcherStub
-            extends ICommunicationDeviceDispatcher.Stub {
+            extends ICommunicationDeviceDispatcher.Stub implements CallbackUtil.DispatcherStub {
 
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -7784,10 +7785,9 @@
         }
 
         @Override
-        @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchCommunicationDeviceChanged(int portId) {
             AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
-            CallbackUtil.callListeners(mCommDevListeners, mCommDevListenerLock,
+            mCommDeviceChangedListenerMgr.callListeners(
                     (listener) -> listener.onCommunicationDeviceChanged(device));
         }
     }
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index 19ea2de..d1bb41e 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -34,7 +34,7 @@
     /** @hide */
     @IntDef({
             BluetoothProfile.A2DP,
-            BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper
+            BluetoothProfile.A2DP_SINK,
             BluetoothProfile.HEADSET, // Can only be set by BtHelper
             BluetoothProfile.HEARING_AID,
             BluetoothProfile.LE_AUDIO,
@@ -105,6 +105,16 @@
     }
 
     /**
+     * Constructor for A2dp sink info
+     * The {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     *
+     * @param volume of device -1 to ignore value
+     */
+    public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
+        return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+    }
+
+    /**
      * Constructor for hearing aid info
      *
      * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
diff --git a/media/java/android/media/CallbackUtil.java b/media/java/android/media/CallbackUtil.java
index ac39317..2b5fd25 100644
--- a/media/java/android/media/CallbackUtil.java
+++ b/media/java/android/media/CallbackUtil.java
@@ -18,11 +18,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.media.permission.ClearCallingIdentityContext;
 import android.media.permission.SafeCloseable;
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -221,4 +224,87 @@
         }
 
     }
+
+    /**
+     * Interface to be implemented by stub implementation for the events received from a server
+     * to the class managing the listener API.
+     * For an example see {@link AudioManager#ModeDispatcherStub} which registers with AudioService.
+     */
+    interface DispatcherStub {
+        /**
+         * Register/unregister the stub as a listener of the events to be forwarded to the listeners
+         * managed by LazyListenerManager.
+         * @param register true for registering, false to unregister
+         */
+        void register(boolean register);
+    }
+
+    /**
+     * Class to manage a list of listeners and their callback, and the associated stub which
+     * receives the events to be forwarded to the listeners.
+     * The list of listeners and the stub and its registration are lazily initialized and registered
+     * @param <T> the listener class
+     */
+    static class LazyListenerManager<T> {
+        private final Object mListenerLock = new Object();
+
+        @GuardedBy("mListenerLock")
+        private @Nullable ArrayList<ListenerInfo<T>> mListeners;
+
+        @GuardedBy("mListenerLock")
+        private @Nullable DispatcherStub mDispatcherStub;
+
+        LazyListenerManager() {
+            // nothing to initialize as instances of dispatcher and list of listeners
+            // are lazily initialized
+        }
+
+        /**
+         * Add a new listener / executor pair for the configured listener
+         * @param executor Executor for the callback
+         * @param listener the listener to register
+         * @param methodName the name of the method calling this utility method for easier to read
+         *          exception messages
+         * @param newStub how to build a new instance of the stub receiving the events when the
+         *          number of listeners goes from 0 to 1, not called until then.
+         */
+        void addListener(@NonNull Executor executor, @NonNull T listener, String methodName,
+                @NonNull java.util.function.Supplier<DispatcherStub> newStub) {
+            synchronized (mListenerLock) {
+                final Pair<ArrayList<ListenerInfo<T>>, DispatcherStub> res =
+                        CallbackUtil.addListener(methodName,
+                                executor, listener, mListeners, mDispatcherStub,
+                                newStub,
+                                stub -> stub.register(true));
+                mListeners = res.first;
+                mDispatcherStub = res.second;
+            }
+        }
+
+        /**
+         * Remove a previously registered listener
+         * @param listener the listener to unregister
+         * @param methodName the name of the method calling this utility method for easier to read
+         *          exception messages
+         */
+        void removeListener(@NonNull T listener, String methodName) {
+            synchronized (mListenerLock) {
+                final Pair<ArrayList<ListenerInfo<T>>, DispatcherStub> res =
+                        CallbackUtil.removeListener(methodName,
+                                listener, mListeners, mDispatcherStub,
+                                stub -> stub.register(false));
+                mListeners = res.first;
+                mDispatcherStub = res.second;
+            }
+        }
+
+        /**
+         * Call the registered listeners with the given callback method
+         * @param callback the listener method to invoke
+         */
+        @SuppressLint("GuardedBy") // lock applied inside callListeners method
+        void callListeners(CallbackMethod<T> callback) {
+            CallbackUtil.callListeners(mListeners, mListenerLock, callback);
+        }
+    }
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7f6fb90..96199a9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -308,6 +308,8 @@
 
     List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
 
+    List<AudioDeviceAttributes> getDevicesForAttributesUnprotected(in AudioAttributes attributes);
+
     int setAllowedCapturePolicy(in int capturePolicy);
 
     int getAllowedCapturePolicy();
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 69ce8d2..09d7fbd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -1124,6 +1124,8 @@
     private class SurfaceImage extends android.media.Image {
         public SurfaceImage(int format) {
             mFormat = format;
+            mHardwareBufferFormat = ImageReader.this.mHardwareBufferFormat;
+            mDataSpace = ImageReader.this.mDataSpace;
         }
 
         SurfaceImage(int hardwareBufferFormat, long dataSpace) {
@@ -1229,6 +1231,12 @@
         }
 
         @Override
+        public long getDataSpace() {
+            throwISEIfImageIsInvalid();
+            return mDataSpace;
+        }
+
+        @Override
         public void setTimestamp(long timestampNs) {
             throwISEIfImageIsInvalid();
             mTimestamp = timestampNs;
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index c0793ec..030d212 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -29,7 +29,6 @@
 import android.media.permission.SafeCloseable;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -216,6 +215,29 @@
     public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2;
 
     /**
+     * @hide
+     * Head tracking mode to string conversion
+     * @param mode a valid head tracking mode
+     * @return a string containing the matching constant name
+     */
+    public static final String headtrackingModeToString(int mode) {
+        switch(mode) {
+            case HEAD_TRACKING_MODE_UNSUPPORTED:
+                return "HEAD_TRACKING_MODE_UNSUPPORTED";
+            case HEAD_TRACKING_MODE_DISABLED:
+                return "HEAD_TRACKING_MODE_DISABLED";
+            case HEAD_TRACKING_MODE_OTHER:
+                return "HEAD_TRACKING_MODE_OTHER";
+            case HEAD_TRACKING_MODE_RELATIVE_WORLD:
+                return "HEAD_TRACKING_MODE_RELATIVE_WORLD";
+            case HEAD_TRACKING_MODE_RELATIVE_DEVICE:
+                return "HEAD_TRACKING_MODE_RELATIVE_DEVICE";
+            default:
+                return "head tracking mode unknown " + mode;
+        }
+    }
+
+    /**
      * Return the level of support for the spatialization feature on this device.
      * This level of support is independent of whether the {@code Spatializer} is currently
      * enabled or available and will not change over time.
@@ -376,16 +398,8 @@
     public void addOnSpatializerStateChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnSpatializerStateChangedListener listener) {
-        synchronized (mStateListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnSpatializerStateChangedListener>>,
-                    SpatializerInfoDispatcherStub> res =
-                    CallbackUtil.addListener("addOnSpatializerStateChangedListener",
-                            executor, listener, mStateListeners, mInfoDispatcherStub,
-                            () -> new SpatializerInfoDispatcherStub(),
-                            stub -> stub.register(true));
-            mStateListeners = res.first;
-            mInfoDispatcherStub = res.second;
-        }
+        mStateListenerMgr.addListener(executor, listener, "addOnSpatializerStateChangedListener",
+                () -> new SpatializerInfoDispatcherStub());
     }
 
     /**
@@ -396,15 +410,7 @@
      */
     public void removeOnSpatializerStateChangedListener(
             @NonNull OnSpatializerStateChangedListener listener) {
-        synchronized (mStateListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnSpatializerStateChangedListener>>,
-                    SpatializerInfoDispatcherStub> res =
-                    CallbackUtil.removeListener("removeOnSpatializerStateChangedListener",
-                            listener, mStateListeners, mInfoDispatcherStub,
-                            stub -> stub.register(false));
-            mStateListeners = res.first;
-            mInfoDispatcherStub = res.second;
-        }
+        mStateListenerMgr.removeListener(listener, "removeOnSpatializerStateChangedListener");
     }
 
     /**
@@ -459,18 +465,16 @@
         }
     }
 
-    private final Object mStateListenerLock = new Object();
     /**
-     * List of listeners for state listener and their associated Executor.
-     * List is lazy-initialized on first registration
+     * manages the OnSpatializerStateChangedListener listeners and the
+     * SpatializerInfoDispatcherStub
      */
-    @GuardedBy("mStateListenerLock")
-    private @Nullable ArrayList<ListenerInfo<OnSpatializerStateChangedListener>> mStateListeners;
+    private final CallbackUtil.LazyListenerManager<OnSpatializerStateChangedListener>
+            mStateListenerMgr = new CallbackUtil.LazyListenerManager();
 
-    @GuardedBy("mStateListenerLock")
-    private @Nullable SpatializerInfoDispatcherStub mInfoDispatcherStub;
-
-    private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub {
+    private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub
+            implements CallbackUtil.DispatcherStub {
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -486,7 +490,7 @@
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerEnabledChanged(boolean enabled) {
-            CallbackUtil.callListeners(mStateListeners, mStateListenerLock,
+            mStateListenerMgr.callListeners(
                     (listener) -> listener.onSpatializerEnabledChanged(
                             Spatializer.this, enabled));
         }
@@ -494,7 +498,7 @@
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerAvailableChanged(boolean available) {
-            CallbackUtil.callListeners(mStateListeners, mStateListenerLock,
+            mStateListenerMgr.callListeners(
                     (listener) -> listener.onSpatializerAvailableChanged(
                             Spatializer.this, available));
         }
@@ -612,16 +616,9 @@
     public void addOnHeadTrackingModeChangedListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnHeadTrackingModeChangedListener listener) {
-        synchronized (mHeadTrackingListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>,
-                    SpatializerHeadTrackingDispatcherStub> res = CallbackUtil.addListener(
-                        "addOnHeadTrackingModeChangedListener", executor, listener,
-                        mHeadTrackingListeners, mHeadTrackingDispatcherStub,
-                        () -> new SpatializerHeadTrackingDispatcherStub(),
-                        stub -> stub.register(true));
-            mHeadTrackingListeners = res.first;
-            mHeadTrackingDispatcherStub = res.second;
-        }
+        mHeadTrackingListenerMgr.addListener(executor, listener,
+                "addOnHeadTrackingModeChangedListener",
+                 () -> new SpatializerHeadTrackingDispatcherStub());
     }
 
     /**
@@ -634,15 +631,8 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
     public void removeOnHeadTrackingModeChangedListener(
             @NonNull OnHeadTrackingModeChangedListener listener) {
-        synchronized (mHeadTrackingListenerLock) {
-            final Pair<ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>,
-                    SpatializerHeadTrackingDispatcherStub> res = CallbackUtil.removeListener(
-                        "removeOnHeadTrackingModeChangedListener", listener,
-                        mHeadTrackingListeners, mHeadTrackingDispatcherStub,
-                        stub -> stub.register(false));
-            mHeadTrackingListeners = res.first;
-            mHeadTrackingDispatcherStub = res.second;
-        }
+        mHeadTrackingListenerMgr.removeListener(listener,
+                "removeOnHeadTrackingModeChangedListener");
     }
 
     /**
@@ -828,20 +818,17 @@
     //-----------------------------------------------------------------------------
     // head tracking callback management and stub
 
-    private final Object mHeadTrackingListenerLock = new Object();
     /**
-     * List of listeners for head tracking mode listener and their associated Executor.
-     * List is lazy-initialized on first registration
+     * manages the OnHeadTrackingModeChangedListener listeners and the
+     * SpatializerHeadTrackingDispatcherStub
      */
-    @GuardedBy("mHeadTrackingListenerLock")
-    private @Nullable ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>
-            mHeadTrackingListeners;
-
-    @GuardedBy("mHeadTrackingListenerLock")
-    private @Nullable SpatializerHeadTrackingDispatcherStub mHeadTrackingDispatcherStub;
+    private final CallbackUtil.LazyListenerManager<OnHeadTrackingModeChangedListener>
+            mHeadTrackingListenerMgr = new CallbackUtil.LazyListenerManager();
 
     private final class SpatializerHeadTrackingDispatcherStub
-            extends ISpatializerHeadTrackingModeCallback.Stub {
+            extends ISpatializerHeadTrackingModeCallback.Stub
+            implements CallbackUtil.DispatcherStub {
+        @Override
         public void register(boolean register) {
             try {
                 if (register) {
@@ -857,14 +844,14 @@
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerActualHeadTrackingModeChanged(int mode) {
-            CallbackUtil.callListeners(mHeadTrackingListeners, mHeadTrackingListenerLock,
+            mHeadTrackingListenerMgr.callListeners(
                     (listener) -> listener.onHeadTrackingModeChanged(Spatializer.this, mode));
         }
 
         @Override
         @SuppressLint("GuardedBy") // lock applied inside callListeners method
         public void dispatchSpatializerDesiredHeadTrackingModeChanged(int mode) {
-            CallbackUtil.callListeners(mHeadTrackingListeners, mHeadTrackingListenerLock,
+            mHeadTrackingListenerMgr.callListeners(
                     (listener) -> listener.onDesiredHeadTrackingModeChanged(
                             Spatializer.this, mode));
         }
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index 85ad3cd..6ba9133 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -49,8 +49,10 @@
                             return StreamEventRequest.createFromParcelBody(source);
                         case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
                             return DsmccRequest.createFromParcelBody(source);
-                        case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+                        case TvInputManager.BROADCAST_INFO_TYPE_COMMAND:
                             return CommandRequest.createFromParcelBody(source);
+                        case TvInputManager.BROADCAST_INFO_TYPE_TIMELINE:
+                            return TimelineRequest.createFromParcelBody(source);
                         default:
                             throw new IllegalStateException(
                                     "Unexpected broadcast info request type (value "
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index e423aba..67bdedc 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -50,8 +50,10 @@
                             return StreamEventResponse.createFromParcelBody(source);
                         case TvInputManager.BROADCAST_INFO_TYPE_DSMCC:
                             return DsmccResponse.createFromParcelBody(source);
-                        case TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION:
+                        case TvInputManager.BROADCAST_INFO_TYPE_COMMAND:
                             return CommandResponse.createFromParcelBody(source);
+                        case TvInputManager.BROADCAST_INFO_TYPE_TIMELINE:
+                            return TimelineResponse.createFromParcelBody(source);
                         default:
                             throw new IllegalStateException(
                                     "Unexpected broadcast info response type (value "
diff --git a/media/java/android/media/tv/CommandRequest.java b/media/java/android/media/tv/CommandRequest.java
index 2391fa3..d61c858 100644
--- a/media/java/android/media/tv/CommandRequest.java
+++ b/media/java/android/media/tv/CommandRequest.java
@@ -23,7 +23,7 @@
 /** @hide */
 public final class CommandRequest extends BroadcastInfoRequest implements Parcelable {
     public static final @TvInputManager.BroadcastInfoType int requestType =
-            TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
+            TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
 
     public static final @NonNull Parcelable.Creator<CommandRequest> CREATOR =
             new Parcelable.Creator<CommandRequest>() {
diff --git a/media/java/android/media/tv/CommandResponse.java b/media/java/android/media/tv/CommandResponse.java
index d34681f..af3d00c 100644
--- a/media/java/android/media/tv/CommandResponse.java
+++ b/media/java/android/media/tv/CommandResponse.java
@@ -23,7 +23,7 @@
 /** @hide */
 public final class CommandResponse extends BroadcastInfoResponse implements Parcelable {
     public static final @TvInputManager.BroadcastInfoType int responseType =
-            TvInputManager.BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION;
+            TvInputManager.BROADCAST_INFO_TYPE_COMMAND;
 
     public static final @NonNull Parcelable.Creator<CommandResponse> CREATOR =
             new Parcelable.Creator<CommandResponse>() {
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index e43d31a..4d49620 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -21,6 +21,9 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** @hide */
 public final class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
     public static final @TvInputManager.BroadcastInfoType int responseType =
@@ -41,20 +44,27 @@
             };
 
     private final ParcelFileDescriptor mFileDescriptor;
+    private final boolean mIsDirectory;
+    private final List<String> mChildren;
 
     public static DsmccResponse createFromParcelBody(Parcel in) {
         return new DsmccResponse(in);
     }
 
     public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
-            ParcelFileDescriptor file) {
+            ParcelFileDescriptor file, boolean isDirectory, List<String> children) {
         super(responseType, requestId, sequence, responseResult);
         mFileDescriptor = file;
+        mIsDirectory = isDirectory;
+        mChildren = children;
     }
 
     protected DsmccResponse(Parcel source) {
         super(responseType, source);
         mFileDescriptor = source.readFileDescriptor();
+        mIsDirectory = (source.readInt() == 1);
+        mChildren = new ArrayList<>();
+        source.readStringList(mChildren);
     }
 
     public ParcelFileDescriptor getFile() {
@@ -65,5 +75,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         mFileDescriptor.writeToParcel(dest, flags);
+        dest.writeInt(mIsDirectory ? 1 : 0);
+        dest.writeStringList(mChildren);
     }
 }
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index f4f55e4..49148ce 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -47,6 +47,7 @@
     void onTimeShiftStartPositionChanged(long timeMs, int seq);
     void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
     void onAitInfoUpdated(in AitInfo aitInfo, int seq);
+    void onSignalStrength(int stength, int seq);
 
     void onTuned(in Uri channelUri, int seq);
     // For the recording session
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 770b8aa..2a33ee6 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -77,7 +77,7 @@
     void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
     void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
 
-    void setIAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
+    void setInteractiveAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
 
     void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
             int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index f427501..9820034 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -42,7 +42,7 @@
     void setCaptionEnabled(boolean enabled);
     void selectTrack(int type, in String trackId);
 
-    void setIAppNotificationEnabled(boolean enable);
+    void setInteractiveAppNotificationEnabled(boolean enable);
 
     void appPrivateCommand(in String action, in Bundle data);
 
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 9830e78..9dfdb78 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -44,6 +44,7 @@
     void onTimeShiftStartPositionChanged(long timeMs);
     void onTimeShiftCurrentPositionChanged(long timeMs);
     void onAitInfoUpdated(in AitInfo aitInfo);
+    void onSignalStrength(int strength);
 
     // For the recording session
     void onTuned(in Uri channelUri);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 418ab2c..8911f6c 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -247,7 +247,7 @@
                 break;
             }
             case DO_SET_IAPP_NOTIFICATION_ENABLED: {
-                mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj);
+                mTvInputSessionImpl.setInteractiveAppNotificationEnabled((Boolean) msg.obj);
                 break;
             }
             case DO_REQUEST_AD: {
@@ -322,7 +322,7 @@
     }
 
     @Override
-    public void setIAppNotificationEnabled(boolean enabled) {
+    public void setInteractiveAppNotificationEnabled(boolean enabled) {
         mCaller.executeOrSendMessage(
                 mCaller.obtainMessageO(DO_SET_IAPP_NOTIFICATION_ENABLED, enabled));
     }
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 33acd0d..fa04293 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -1,6 +1,6 @@
-nchalko@google.com
 quxiangfang@google.com
 shubang@google.com
+hgchen@google.com
 
 # For android remote service
 per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index 912cbce..68d5f8a 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -17,9 +17,9 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.net.Uri;
 
 /** @hide */
 public final class TableResponse extends BroadcastInfoResponse implements Parcelable {
diff --git a/media/java/android/media/tv/TimelineRequest.java b/media/java/android/media/tv/TimelineRequest.java
new file mode 100644
index 0000000..0714972
--- /dev/null
+++ b/media/java/android/media/tv/TimelineRequest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class TimelineRequest extends BroadcastInfoRequest implements Parcelable {
+    private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE =
+            TvInputManager.BROADCAST_INFO_TYPE_TIMELINE;
+
+    public static final @NonNull Parcelable.Creator<TimelineRequest> CREATOR =
+            new Parcelable.Creator<TimelineRequest>() {
+                @Override
+                public TimelineRequest createFromParcel(Parcel source) {
+                    source.readInt();
+                    return createFromParcelBody(source);
+                }
+
+                @Override
+                public TimelineRequest[] newArray(int size) {
+                    return new TimelineRequest[size];
+                }
+            };
+
+    private final int mIntervalMs;
+
+    static TimelineRequest createFromParcelBody(Parcel in) {
+        return new TimelineRequest(in);
+    }
+
+    public TimelineRequest(int requestId, @RequestOption int option, int intervalMs) {
+        super(REQUEST_TYPE, requestId, option);
+        mIntervalMs = intervalMs;
+    }
+
+    protected TimelineRequest(Parcel source) {
+        super(REQUEST_TYPE, source);
+        mIntervalMs = source.readInt();
+    }
+
+    public int getIntervalMs() {
+        return mIntervalMs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mIntervalMs);
+    }
+}
diff --git a/media/java/android/media/tv/TimelineResponse.java b/media/java/android/media/tv/TimelineResponse.java
new file mode 100644
index 0000000..fee10b4
--- /dev/null
+++ b/media/java/android/media/tv/TimelineResponse.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class TimelineResponse extends BroadcastInfoResponse implements Parcelable {
+    private static final @TvInputManager.BroadcastInfoType int RESPONSE_TYPE =
+            TvInputManager.BROADCAST_INFO_TYPE_TIMELINE;
+
+    public static final @NonNull Parcelable.Creator<TimelineResponse> CREATOR =
+            new Parcelable.Creator<TimelineResponse>() {
+                @Override
+                public TimelineResponse createFromParcel(Parcel source) {
+                    source.readInt();
+                    return createFromParcelBody(source);
+                }
+
+                @Override
+                public TimelineResponse[] newArray(int size) {
+                    return new TimelineResponse[size];
+                }
+            };
+
+    private final String mSelector;
+    private final int mUnitsPerTick;
+    private final int mUnitsPerSecond;
+    private final long mWallClock;
+    private final long mTicks;
+
+    static TimelineResponse createFromParcelBody(Parcel in) {
+        return new TimelineResponse(in);
+    }
+
+    public TimelineResponse(int requestId, int sequence,
+            @ResponseResult int responseResult, String selector, int unitsPerTick,
+            int unitsPerSecond, long wallClock, long ticks) {
+        super(RESPONSE_TYPE, requestId, sequence, responseResult);
+        mSelector = selector;
+        mUnitsPerTick = unitsPerTick;
+        mUnitsPerSecond = unitsPerSecond;
+        mWallClock = wallClock;
+        mTicks = ticks;
+    }
+
+    protected TimelineResponse(Parcel source) {
+        super(RESPONSE_TYPE, source);
+        mSelector = source.readString();
+        mUnitsPerTick = source.readInt();
+        mUnitsPerSecond = source.readInt();
+        mWallClock = source.readLong();
+        mTicks = source.readLong();
+    }
+
+    public String getSelector() {
+        return mSelector;
+    }
+
+    public int getUnitsPerTick() {
+        return mUnitsPerTick;
+    }
+
+    public int getUnitsPerSecond() {
+        return mUnitsPerSecond;
+    }
+
+    public long getWallClock() {
+        return mWallClock;
+    }
+
+    public long getTicks() {
+        return mTicks;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mSelector);
+        dest.writeInt(mUnitsPerTick);
+        dest.writeInt(mUnitsPerSecond);
+        dest.writeLong(mWallClock);
+        dest.writeLong(mTicks);
+    }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 52036b0..98d1599 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -27,6 +28,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat.Encoding;
 import android.media.PlaybackParams;
 import android.media.tv.interactive.TvIAppManager;
 import android.net.Uri;
@@ -60,6 +63,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -359,9 +363,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
+    @IntDef(prefix = "BROADCAST_INFO_TYPE_", value =
+            {BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION,
             BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC,
-            BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION})
+            BROADCAST_INFO_TYPE_COMMAND, BROADCAST_INFO_TYPE_TIMELINE})
     public @interface BroadcastInfoType {}
 
     /** @hide */
@@ -377,7 +382,31 @@
     /** @hide */
     public static final int BROADCAST_INFO_TYPE_DSMCC = 6;
     /** @hide */
-    public static final int BROADCAST_INFO_TYPE_TV_PROPRIETARY_FUNCTION = 7;
+    public static final int BROADCAST_INFO_TYPE_COMMAND = 7;
+    /** @hide */
+    public static final int BROADCAST_INFO_TYPE_TIMELINE = 8;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SIGNAL_STRENGTH_",
+            value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG})
+    public @interface SignalStrength {}
+
+    /**
+     * Signal lost.
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_LOST = 1;
+    /**
+     * Weak signal.
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_WEAK = 2;
+    /**
+     * Strong signal.
+     * @hide
+     */
+    public static final int SIGNAL_STRENGTH_STRONG = 3;
 
     /**
      * An unknown state of the client pid gets from the TvInputManager. Client gets this value when
@@ -658,6 +687,14 @@
         }
 
         /**
+         * This is called when signal strength is updated.
+         * @param session A {@link TvInputManager.Session} associated with this callback.
+         * @param strength The current signal strength.
+         */
+        public void onSignalStrength(Session session, @SignalStrength int strength) {
+        }
+
+        /**
          * This is called when the session has been tuned to the given channel.
          *
          * @param channelUri The URI of a channel.
@@ -731,8 +768,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onTracksChanged(mSession, tracks);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyTracksChanged(tracks);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyTracksChanged(tracks);
                     }
                 }
             });
@@ -743,8 +781,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onTrackSelected(mSession, type, trackId);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyTrackSelected(type, trackId);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyTrackSelected(type, trackId);
                     }
                 }
             });
@@ -764,8 +803,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onVideoAvailable(mSession);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyVideoAvailable();
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyVideoAvailable();
                     }
                 }
             });
@@ -776,8 +816,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onVideoUnavailable(mSession, reason);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyVideoUnavailable(reason);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyVideoUnavailable(reason);
                     }
                 }
             });
@@ -788,8 +829,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onContentAllowed(mSession);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyContentAllowed();
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyContentAllowed();
                     }
                 }
             });
@@ -800,8 +842,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onContentBlocked(mSession, rating);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyContentBlocked(rating);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyContentBlocked(rating);
                     }
                 }
             });
@@ -862,13 +905,27 @@
             });
         }
 
+        void postSignalStrength(final int strength) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSignalStrength(mSession, strength);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifySignalStrength(strength);
+                    }
+                }
+            });
+        }
+
         void postTuned(final Uri channelUri) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     mSessionCallback.onTuned(mSession, channelUri);
-                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
-                        mSession.getIAppSession().notifyTuned(channelUri);
+                    if (mSession.mIAppNotificationEnabled
+                            && mSession.getInteractiveAppSession() != null) {
+                        mSession.getInteractiveAppSession().notifyTuned(channelUri);
                     }
                 }
             });
@@ -899,8 +956,9 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        if (mSession.getIAppSession() != null) {
-                            mSession.getIAppSession().notifyBroadcastInfoResponse(response);
+                        if (mSession.getInteractiveAppSession() != null) {
+                            mSession.getInteractiveAppSession()
+                                    .notifyBroadcastInfoResponse(response);
                         }
                     }
                 });
@@ -912,8 +970,8 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        if (mSession.getIAppSession() != null) {
-                            mSession.getIAppSession().notifyAdResponse(response);
+                        if (mSession.getInteractiveAppSession() != null) {
+                            mSession.getInteractiveAppSession().notifyAdResponse(response);
                         }
                     }
                 });
@@ -1295,6 +1353,18 @@
             }
 
             @Override
+            public void onSignalStrength(int strength, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postSignalStrength(strength);
+                }
+            }
+
+            @Override
             public void onTuned(Uri channelUri, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -2261,12 +2331,12 @@
             mSessionCallbackRecordMap = sessionCallbackRecordMap;
         }
 
-        public TvIAppManager.Session getIAppSession() {
+        public TvIAppManager.Session getInteractiveAppSession() {
             return mIAppSession;
         }
 
-        public void setIAppSession(TvIAppManager.Session IAppSession) {
-            this.mIAppSession = IAppSession;
+        public void setInteractiveAppSession(TvIAppManager.Session iAppSession) {
+            this.mIAppSession = iAppSession;
         }
 
         /**
@@ -2527,13 +2597,13 @@
          *                {@code false} otherwise.
          * @hide
          */
-        public void setIAppNotificationEnabled(boolean enabled) {
+        public void setInteractiveAppNotificationEnabled(boolean enabled) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.setIAppNotificationEnabled(mToken, enabled, mUserId);
+                mService.setInteractiveAppNotificationEnabled(mToken, enabled, mUserId);
                 mIAppNotificationEnabled = enabled;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -3221,6 +3291,16 @@
             return false;
         }
 
+        /**
+         * Override default audio sink from audio policy.
+         *
+         * @param audioType device type of the audio sink to override with.
+         * @param audioAddress device address of the audio sink to override with.
+         * @param samplingRate desired sampling rate. Use default when it's 0.
+         * @param channelMask desired channel mask. Use default when it's
+         *        AudioFormat.CHANNEL_OUT_DEFAULT.
+         * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+         */
         public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
                 int channelMask, int format) {
             try {
@@ -3230,5 +3310,27 @@
                 throw new RuntimeException(e);
             }
         }
+
+        /**
+         * Override default audio sink from audio policy.
+         *
+         * @param device {@link android.media.AudioDeviceInfo} to use.
+         * @param samplingRate desired sampling rate. Use default when it's 0.
+         * @param channelMask desired channel mask. Use default when it's
+         *        AudioFormat.CHANNEL_OUT_DEFAULT.
+         * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+         */
+        public void overrideAudioSink(@NonNull AudioDeviceInfo device,
+                @IntRange(from = 0) int samplingRate,
+                int channelMask, @Encoding int format) {
+            Objects.requireNonNull(device);
+            try {
+                mInterface.overrideAudioSink(
+                        AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
+                        device.getAddress(), samplingRate, channelMask, format);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 3a40d6f..524ba34 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -966,6 +966,27 @@
         }
 
         /**
+         * Notifies signal strength.
+         * @hide
+         */
+        public void notifySignalStrength(@TvInputManager.SignalStrength final int strength) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifySignalStrength");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onSignalStrength(strength);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifySignalStrength", e);
+                    }
+                }
+            });
+        }
+
+        /**
          * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
          * is relative to the overlay view that sits on top of this surface.
          *
@@ -1180,7 +1201,7 @@
          * @param enabled {@code true} to enable, {@code false} to disable.
          * @hide
          */
-        public void onSetIAppNotificationEnabled(boolean enabled) {
+        public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
         }
 
         /**
@@ -1532,10 +1553,10 @@
         }
 
         /**
-         * Calls {@link #onSetIAppNotificationEnabled}.
+         * Calls {@link #onSetInteractiveAppNotificationEnabled}.
          */
-        void setIAppNotificationEnabled(boolean enabled) {
-            onSetIAppNotificationEnabled(enabled);
+        void setInteractiveAppNotificationEnabled(boolean enabled) {
+            onSetInteractiveAppNotificationEnabled(enabled);
         }
 
         /**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 4a12cd7..71f6ad6 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -486,9 +486,9 @@
      *                {@code false} otherwise.
      * @hide
      */
-    public void setIAppNotificationEnabled(boolean enabled) {
+    public void setInteractiveAppNotificationEnabled(boolean enabled) {
         if (mSession != null) {
-            mSession.setIAppNotificationEnabled(enabled);
+            mSession.setInteractiveAppNotificationEnabled(enabled);
         }
     }
 
@@ -1071,6 +1071,16 @@
         }
 
         /**
+         * This is called when signal strength is updated.
+         * @param inputId The ID of the TV input bound to this view.
+         * @param strength The current signal strength.
+         *
+         * @hide
+         */
+        public void onSignalStrength(String inputId, @TvInputManager.SignalStrength int strength) {
+        }
+
+        /**
          * This is called when the session has been tuned to the given channel.
          *
          * @param channelUri The URI of a channel.
@@ -1390,6 +1400,20 @@
         }
 
         @Override
+        public void onSignalStrength(Session session, int strength) {
+            if (DEBUG) {
+                Log.d(TAG, "onSignalStrength(strength=" + strength + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSignalStrength - session not created");
+                return;
+            }
+            if (mCallback != null) {
+                mCallback.onSignalStrength(mInputId, strength);
+            }
+        }
+
+        @Override
         public void onTuned(Session session, Uri channelUri) {
             if (DEBUG) {
                 Log.d(TAG, "onTuned(channelUri=" + channelUri + ")");
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 2e04359..a19a2d2 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -20,9 +20,9 @@
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppClient;
-import android.media.tv.interactive.ITvIAppManagerCallback;
-import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
+import android.media.tv.interactive.TvInteractiveAppInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.view.Surface;
@@ -32,21 +32,25 @@
  * @hide
  */
 interface ITvIAppManager {
-    List<TvIAppInfo> getTvIAppServiceList(int userId);
+    List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
     void prepare(String tiasId, int type, int userId);
-    void notifyAppLinkInfo(String tiasId, in Bundle info, int userId);
+    void registerAppLinkInfo(String tiasId, in Bundle info, int userId);
+    void unregisterAppLinkInfo(String tiasId, in Bundle info, int userId);
     void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
-    void startIApp(in IBinder sessionToken, int userId);
-    void stopIApp(in IBinder sessionToken, int userId);
+    void startInteractiveApp(in IBinder sessionToken, int userId);
+    void stopInteractiveApp(in IBinder sessionToken, int userId);
+    void resetInteractiveApp(in IBinder sessionToken, int userId);
     void createBiInteractiveApp(
             in IBinder sessionToken, in Uri biIAppUri, in Bundle params, int userId);
     void destroyBiInteractiveApp(in IBinder sessionToken, in String biIAppId, int userId);
+    void setTeletextAppEnabled(in IBinder sessionToken, boolean enable, int userId);
     void sendCurrentChannelUri(in IBinder sessionToken, in Uri channelUri, int userId);
     void sendCurrentChannelLcn(in IBinder sessionToken, int lcn, int userId);
     void sendStreamVolume(in IBinder sessionToken, float volume, int userId);
     void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
-    void createSession(
-            in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
+    void sendCurrentTvInputId(in IBinder sessionToken, in String inputId, int userId);
+    void createSession(in ITvInteractiveAppClient client, in String iAppServiceId, int type,
+            int seq, int userId);
     void releaseSession(in IBinder sessionToken, int userId);
     void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId);
     void notifyTrackSelected(in IBinder sessionToken, int type, in String trackId, int userId);
@@ -55,6 +59,7 @@
     void notifyVideoUnavailable(in IBinder sessionToken, int reason, int userId);
     void notifyContentAllowed(in IBinder sessionToken, int userId);
     void notifyContentBlocked(in IBinder sessionToken, in String rating, int userId);
+    void notifySignalStrength(in IBinder sessionToken, int stength, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
     void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
             int userId);
@@ -67,6 +72,6 @@
     void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId);
     void removeMediaView(in IBinder sessionToken, int userId);
 
-    void registerCallback(in ITvIAppManagerCallback callback, int userId);
-    void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
+    void registerCallback(in ITvInteractiveAppManagerCallback callback, int userId);
+    void unregisterCallback(in ITvInteractiveAppManagerCallback callback, int userId);
 }
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
deleted file mode 100644
index 8acb75f..0000000
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.media.tv.interactive.ITvIAppServiceCallback;
-import android.media.tv.interactive.ITvIAppSessionCallback;
-import android.os.Bundle;
-import android.view.InputChannel;
-
-/**
- * Top-level interface to a TV IApp component (implemented in a Service). It's used for
- * TvIAppManagerService to communicate with TvIAppService.
- * @hide
- */
-oneway interface ITvIAppService {
-    void registerCallback(in ITvIAppServiceCallback callback);
-    void unregisterCallback(in ITvIAppServiceCallback callback);
-    void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
-            in String iAppServiceId, int type);
-    void prepare(int type);
-    void notifyAppLinkInfo(in Bundle info);
-    void sendAppLinkCommand(in Bundle command);
-}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
similarity index 83%
rename from media/java/android/media/tv/interactive/ITvIAppClient.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 892a800..1a8fc46 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -24,11 +24,11 @@
 import android.view.InputChannel;
 
 /**
- * Interface a client of the ITvIAppManager implements, to identify itself and receive information
- * about changes to the state of each TV interactive application service.
+ * Interface a client of the ITvInteractiveAppManager implements, to identify itself and receive
+ * information about changes to the state of each TV interactive application service.
  * @hide
  */
-oneway interface ITvIAppClient {
+oneway interface ITvInteractiveAppClient {
     void onSessionCreated(in String iAppServiceId, IBinder token, in InputChannel channel, int seq);
     void onSessionReleased(int seq);
     void onLayoutSurface(int left, int top, int right, int bottom, int seq);
@@ -36,11 +36,13 @@
     void onRemoveBroadcastInfo(int id, int seq);
     void onSessionStateChanged(int state, int seq);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
+    void onTeletextAppStateChanged(int state, int seq);
     void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
     void onSetVideoBounds(in Rect rect, int seq);
     void onRequestCurrentChannelUri(int seq);
     void onRequestCurrentChannelLcn(int seq);
     void onRequestStreamVolume(int seq);
     void onRequestTrackInfoList(int seq);
+    void onRequestCurrentTvInputId(int seq);
     void onAdRequest(in AdRequest request, int Seq);
 }
diff --git a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
similarity index 62%
rename from media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
index d5e0c63..f4510f6 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
@@ -16,16 +16,16 @@
 
 package android.media.tv.interactive;
 
-import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.interactive.TvInteractiveAppInfo;
 
 /**
- * Interface to receive callbacks from ITvIAppManager regardless of sessions.
+ * Interface to receive callbacks from ITvInteractiveAppManager regardless of sessions.
  * @hide
  */
-interface ITvIAppManagerCallback {
-    void onIAppServiceAdded(in String iAppServiceId);
-    void onIAppServiceRemoved(in String iAppServiceId);
-    void onIAppServiceUpdated(in String iAppServiceId);
-    void onTvIAppInfoUpdated(in TvIAppInfo tvIAppInfo);
+interface ITvInteractiveAppManagerCallback {
+    void onInteractiveAppServiceAdded(in String iAppServiceId);
+    void onInteractiveAppServiceRemoved(in String iAppServiceId);
+    void onInteractiveAppServiceUpdated(in String iAppServiceId);
+    void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo);
     void onStateChanged(in String iAppServiceId, int type, int state);
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
new file mode 100644
index 0000000..c1e6622
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
+import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
+import android.os.Bundle;
+import android.view.InputChannel;
+
+/**
+ * Top-level interface to a TV Interactive App component (implemented in a Service). It's used for
+ * TvIAppManagerService to communicate with TvIAppService.
+ * @hide
+ */
+oneway interface ITvInteractiveAppService {
+    void registerCallback(in ITvInteractiveAppServiceCallback callback);
+    void unregisterCallback(in ITvInteractiveAppServiceCallback callback);
+    void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback,
+            in String iAppServiceId, int type);
+    void prepare(int type);
+    void registerAppLinkInfo(in Bundle info);
+    void unregisterAppLinkInfo(in Bundle info);
+    void sendAppLinkCommand(in Bundle command);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
similarity index 84%
rename from media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
index fec7d78..f56d3bd 100644
--- a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
@@ -17,10 +17,10 @@
 package android.media.tv.interactive;
 
 /**
- * Helper interface for ITvIAppService to allow the TvIAppService to notify the
+ * Helper interface for ITvInteractiveAppService to allow the TvIAppService to notify the
  * TvIAppManagerService.
  * @hide
  */
-oneway interface ITvIAppServiceCallback {
+oneway interface ITvInteractiveAppServiceCallback {
     void onStateChanged(int type, int state);
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
similarity index 83%
rename from media/java/android/media/tv/interactive/ITvIAppSession.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index 2788ff6..c449d2475 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -26,18 +26,22 @@
 import android.view.Surface;
 
 /**
- * Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
+ * Sub-interface of ITvInteractiveAppService.aidl which is created per session and has its own
+ * context.
  * @hide
  */
-oneway interface ITvIAppSession {
-    void startIApp();
-    void stopIApp();
+oneway interface ITvInteractiveAppSession {
+    void startInteractiveApp();
+    void stopInteractiveApp();
+    void resetInteractiveApp();
     void createBiInteractiveApp(in Uri biIAppUri, in Bundle params);
     void destroyBiInteractiveApp(in String biIAppId);
+    void setTeletextAppEnabled(boolean enable);
     void sendCurrentChannelUri(in Uri channelUri);
     void sendCurrentChannelLcn(int lcn);
     void sendStreamVolume(float volume);
     void sendTrackInfoList(in List<TvTrackInfo> tracks);
+    void sendCurrentTvInputId(in String inputId);
     void release();
     void notifyTuned(in Uri channelUri);
     void notifyTrackSelected(int type, in String trackId);
@@ -46,6 +50,7 @@
     void notifyVideoUnavailable(int reason);
     void notifyContentAllowed();
     void notifyContentBlocked(in String rating);
+    void notifySignalStrength(int strength);
     void setSurface(in Surface surface);
     void dispatchSurfaceChanged(int format, int width, int height);
     void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
similarity index 78%
rename from media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 9b9e6af..c270424 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -19,27 +19,29 @@
 import android.graphics.Rect;
 import android.media.tv.AdRequest;
 import android.media.tv.BroadcastInfoRequest;
-import android.media.tv.interactive.ITvIAppSession;
+import android.media.tv.interactive.ITvInteractiveAppSession;
 import android.net.Uri;
 import android.os.Bundle;
 
 /**
- * Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
- * there is a related event.
+ * Helper interface for ITvInteractiveAppSession to allow TvIAppService to notify the
+ * system service when there is a related event.
  * @hide
  */
-oneway interface ITvIAppSessionCallback {
-    void onSessionCreated(in ITvIAppSession session);
+oneway interface ITvInteractiveAppSessionCallback {
+    void onSessionCreated(in ITvInteractiveAppSession session);
     void onLayoutSurface(int left, int top, int right, int bottom);
     void onBroadcastInfoRequest(in BroadcastInfoRequest request);
     void onRemoveBroadcastInfo(int id);
     void onSessionStateChanged(int state);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
+    void onTeletextAppStateChanged(int state);
     void onCommandRequest(in String cmdType, in Bundle parameters);
     void onSetVideoBounds(in Rect rect);
     void onRequestCurrentChannelUri();
     void onRequestCurrentChannelLcn();
     void onRequestStreamVolume();
     void onRequestTrackInfoList();
+    void onRequestCurrentTvInputId();
     void onAdRequest(in AdRequest request);
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java
deleted file mode 100644
index b5245fc..0000000
--- a/media/java/android/media/tv/interactive/TvIAppInfo.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.interactive;
-
-import android.annotation.NonNull;
-import android.annotation.StringDef;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is used to specify meta information of a TV interactive app.
- * @hide
- */
-public final class TvIAppInfo implements Parcelable {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "TvIAppInfo";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
-            INTERACTIVE_APP_TYPE_HBBTV,
-            INTERACTIVE_APP_TYPE_ATSC,
-            INTERACTIVE_APP_TYPE_GINGA,
-    })
-    @interface InteractiveAppType {}
-
-    /** HbbTV interactive app type */
-    public static final String INTERACTIVE_APP_TYPE_HBBTV = "hbbtv";
-    /** ATSC interactive app type */
-    public static final String INTERACTIVE_APP_TYPE_ATSC = "atsc";
-    /** Ginga interactive app type */
-    public static final String INTERACTIVE_APP_TYPE_GINGA = "ginga";
-
-    private final ResolveInfo mService;
-    private final String mId;
-    private List<String> mTypes = new ArrayList<>();
-
-    private TvIAppInfo(ResolveInfo service, String id, List<String> types) {
-        mService = service;
-        mId = id;
-        mTypes = types;
-    }
-
-    private TvIAppInfo(@NonNull Parcel in) {
-        mService = ResolveInfo.CREATOR.createFromParcel(in);
-        mId = in.readString();
-        in.readStringList(mTypes);
-    }
-
-    public static final @NonNull Creator<TvIAppInfo> CREATOR = new Creator<TvIAppInfo>() {
-        @Override
-        public TvIAppInfo createFromParcel(Parcel in) {
-            return new TvIAppInfo(in);
-        }
-
-        @Override
-        public TvIAppInfo[] newArray(int size) {
-            return new TvIAppInfo[size];
-        }
-    };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        mService.writeToParcel(dest, flags);
-        dest.writeString(mId);
-        dest.writeStringList(mTypes);
-    }
-
-    @NonNull
-    public String getId() {
-        return mId;
-    }
-
-    /**
-     * Returns the component of the TV IApp service.
-     * @hide
-     */
-    public ComponentName getComponent() {
-        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
-    }
-
-    /**
-     * Returns the information of the service that implements this TV IApp service.
-     */
-    public ServiceInfo getServiceInfo() {
-        return mService.serviceInfo;
-    }
-
-    /**
-     * Gets supported interactive app types
-     */
-    @NonNull
-    public List<String> getSupportedTypes() {
-        return new ArrayList<>(mTypes);
-    }
-
-    /**
-     * A convenience builder for creating {@link TvIAppInfo} objects.
-     */
-    public static final class Builder {
-        private static final String XML_START_TAG_NAME = "tv-iapp";
-        private final Context mContext;
-        private final ResolveInfo mResolveInfo;
-        private final List<String> mTypes = new ArrayList<>();
-
-        /**
-         * Constructs a new builder for {@link TvIAppInfo}.
-         *
-         * @param context A Context of the application package implementing this class.
-         * @param component The name of the application component to be used for the
-         *                  {@link TvIAppService}.
-         */
-        public Builder(@NonNull Context context, @NonNull ComponentName component) {
-            if (context == null) {
-                throw new IllegalArgumentException("context cannot be null.");
-            }
-            Intent intent = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
-            mResolveInfo = context.getPackageManager().resolveService(intent,
-                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
-            if (mResolveInfo == null) {
-                throw new IllegalArgumentException("Invalid component. Can't find the service.");
-            }
-            mContext = context;
-        }
-
-        /**
-         * Creates a {@link TvIAppInfo} instance with the specified fields. Most of the information
-         * is obtained by parsing the AndroidManifest and {@link TvIAppService#SERVICE_META_DATA}
-         * for the {@link TvIAppService} this TV IApp implements.
-         *
-         * @return TvIAppInfo containing information about this TV IApp service.
-         */
-        @NonNull
-        public TvIAppInfo build() {
-            ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
-                    mResolveInfo.serviceInfo.name);
-            String id;
-            id = generateIAppServiceId(componentName);
-            parseServiceMetadata();
-            return new TvIAppInfo(mResolveInfo, id, mTypes);
-        }
-
-        private static String generateIAppServiceId(ComponentName name) {
-            return name.flattenToShortString();
-        }
-
-        private void parseServiceMetadata() {
-            ServiceInfo si = mResolveInfo.serviceInfo;
-            PackageManager pm = mContext.getPackageManager();
-            try (XmlResourceParser parser =
-                         si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
-                if (parser == null) {
-                    throw new IllegalStateException("No " + TvIAppService.SERVICE_META_DATA
-                            + " meta-data found for " + si.name);
-                }
-
-                Resources res = pm.getResourcesForApplication(si.applicationInfo);
-                AttributeSet attrs = Xml.asAttributeSet(parser);
-
-                int type;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && type != XmlPullParser.START_TAG) {
-                    // move to the START_TAG
-                }
-
-                String nodeName = parser.getName();
-                if (!XML_START_TAG_NAME.equals(nodeName)) {
-                    throw new IllegalStateException("Meta-data does not start with "
-                            + XML_START_TAG_NAME + " tag for " + si.name);
-                }
-
-                TypedArray sa = res.obtainAttributes(attrs,
-                        com.android.internal.R.styleable.TvIAppService);
-                CharSequence[] types = sa.getTextArray(
-                        com.android.internal.R.styleable.TvIAppService_supportedTypes);
-                for (CharSequence cs : types) {
-                    mTypes.add(cs.toString().toLowerCase());
-                }
-
-                sa.recycle();
-            } catch (IOException | XmlPullParserException e) {
-                throw new IllegalStateException(
-                        "Failed reading meta-data for " + si.packageName, e);
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new IllegalStateException("No resources found for " + si.packageName, e);
-            }
-        }
-    }
-}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
old mode 100644
new mode 100755
index 9685e3a..f819438
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -64,39 +64,63 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = false, prefix = "TV_IAPP_RTE_STATE_", value = {
-            TV_IAPP_RTE_STATE_UNREALIZED,
-            TV_IAPP_RTE_STATE_PREPARING,
-            TV_IAPP_RTE_STATE_READY,
-            TV_IAPP_RTE_STATE_ERROR})
-    public @interface TvIAppRteState {}
+    @IntDef(flag = false, prefix = "TV_INTERACTIVE_APP_RTE_STATE_", value = {
+            TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED,
+            TV_INTERACTIVE_APP_RTE_STATE_PREPARING,
+            TV_INTERACTIVE_APP_RTE_STATE_READY,
+            TV_INTERACTIVE_APP_RTE_STATE_ERROR})
+    public @interface TvInteractiveAppRteState {}
 
     /**
      * Unrealized state of interactive app RTE.
      * @hide
      */
-    public static final int TV_IAPP_RTE_STATE_UNREALIZED = 1;
+    public static final int TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED = 1;
     /**
      * Preparing state of interactive app RTE.
      * @hide
      */
-    public static final int TV_IAPP_RTE_STATE_PREPARING = 2;
+    public static final int TV_INTERACTIVE_APP_RTE_STATE_PREPARING = 2;
     /**
      * Ready state of interactive app RTE.
      * @hide
      */
-    public static final int TV_IAPP_RTE_STATE_READY = 3;
+    public static final int TV_INTERACTIVE_APP_RTE_STATE_READY = 3;
     /**
      * Error state of interactive app RTE.
      * @hide
      */
-    public static final int TV_IAPP_RTE_STATE_ERROR = 4;
+    public static final int TV_INTERACTIVE_APP_RTE_STATE_ERROR = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "TELETEXT_APP_STATE_", value = {
+            TELETEXT_APP_STATE_SHOW,
+            TELETEXT_APP_STATE_HIDE,
+            TELETEXT_APP_STATE_ERROR})
+    public @interface TeletextAppState {}
+
+    /**
+     * Show state of Teletext app.
+     * @hide
+     */
+    public static final int TELETEXT_APP_STATE_SHOW = 1;
+    /**
+     * Hide state of Teletext app.
+     * @hide
+     */
+    public static final int TELETEXT_APP_STATE_HIDE = 2;
+    /**
+     * Error state of Teletext app.
+     * @hide
+     */
+    public static final int TELETEXT_APP_STATE_ERROR = 3;
 
     /**
      * Key for package name in app link.
      * <p>Type: String
      *
-     * @see #notifyAppLinkInfo(String, Bundle)
+     * @see #registerAppLinkInfo(String, Bundle)
      * @see #sendAppLinkCommand(String, Bundle)
      * @hide
      */
@@ -106,7 +130,7 @@
      * Key for class name in app link.
      * <p>Type: String
      *
-     * @see #notifyAppLinkInfo(String, Bundle)
+     * @see #registerAppLinkInfo(String, Bundle)
      * @see #sendAppLinkCommand(String, Bundle)
      * @hide
      */
@@ -116,7 +140,7 @@
      * Key for URI scheme in app link.
      * <p>Type: String
      *
-     * @see #notifyAppLinkInfo(String, Bundle)
+     * @see #registerAppLinkInfo(String, Bundle)
      * @hide
      */
     public static final String KEY_URI_SCHEME = "uri_scheme";
@@ -125,7 +149,7 @@
      * Key for URI host in app link.
      * <p>Type: String
      *
-     * @see #notifyAppLinkInfo(String, Bundle)
+     * @see #registerAppLinkInfo(String, Bundle)
      * @hide
      */
     public static final String KEY_URI_HOST = "uri_host";
@@ -134,7 +158,7 @@
      * Key for URI prefix in app link.
      * <p>Type: String
      *
-     * @see #notifyAppLinkInfo(String, Bundle)
+     * @see #registerAppLinkInfo(String, Bundle)
      * @hide
      */
     public static final String KEY_URI_PREFIX = "uri_prefix";
@@ -174,7 +198,7 @@
             new SparseArray<>();
 
     // @GuardedBy("mLock")
-    private final List<TvIAppCallbackRecord> mCallbackRecords = new LinkedList<>();
+    private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new LinkedList<>();
 
     // A sequence number for the next session to be created. Should be protected by a lock
     // {@code mSessionCallbackRecordMap}.
@@ -182,13 +206,13 @@
 
     private final Object mLock = new Object();
 
-    private final ITvIAppClient mClient;
+    private final ITvInteractiveAppClient mClient;
 
     /** @hide */
     public TvIAppManager(ITvIAppManager service, int userId) {
         mService = service;
         mUserId = userId;
-        mClient = new ITvIAppClient.Stub() {
+        mClient = new ITvInteractiveAppClient.Stub() {
             @Override
             public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel,
                     int seq) {
@@ -260,8 +284,10 @@
             }
 
             @Override
-            public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
-                    Bundle parameters, int seq) {
+            public void onCommandRequest(
+                    @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                    Bundle parameters,
+                    int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
@@ -345,6 +371,18 @@
             }
 
             @Override
+            public void onRequestCurrentTvInputId(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRequestCurrentTvInputId();
+                }
+            }
+
+            @Override
             public void onSessionStateChanged(int state, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -367,41 +405,54 @@
                     record.postBiInteractiveAppCreated(biIAppUri, biIAppId);
                 }
             }
+
+            @Override
+            public void onTeletextAppStateChanged(int state, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postTeletextAppStateChanged(state);
+                }
+            }
         };
-        ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() {
+        ITvInteractiveAppManagerCallback managerCallback =
+                new ITvInteractiveAppManagerCallback.Stub() {
             @Override
-            public void onIAppServiceAdded(String iAppServiceId) {
+            public void onInteractiveAppServiceAdded(String iAppServiceId) {
                 synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postIAppServiceAdded(iAppServiceId);
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postInteractiveAppServiceAdded(iAppServiceId);
                     }
                 }
             }
 
             @Override
-            public void onIAppServiceRemoved(String iAppServiceId) {
+            public void onInteractiveAppServiceRemoved(String iAppServiceId) {
                 synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postIAppServiceRemoved(iAppServiceId);
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postInteractiveAppServiceRemoved(iAppServiceId);
                     }
                 }
             }
 
             @Override
-            public void onIAppServiceUpdated(String iAppServiceId) {
+            public void onInteractiveAppServiceUpdated(String iAppServiceId) {
                 synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postIAppServiceUpdated(iAppServiceId);
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postInteractiveAppServiceUpdated(iAppServiceId);
                     }
                 }
             }
 
             @Override
-            public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) {
-                // TODO: add public API updateIAppInfo()
+            public void onTvInteractiveAppInfoUpdated(TvInteractiveAppInfo iAppInfo) {
+                // TODO: add public API updateInteractiveAppInfo()
                 synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
-                        record.postTvIAppInfoUpdated(iAppInfo);
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
+                        record.postTvInteractiveAppInfoUpdated(iAppInfo);
                     }
                 }
             }
@@ -409,7 +460,7 @@
             @Override
             public void onStateChanged(String iAppServiceId, int type, int state) {
                 synchronized (mLock) {
-                    for (TvIAppCallbackRecord record : mCallbackRecords) {
+                    for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
                         record.postStateChanged(iAppServiceId, type, state);
                     }
                 }
@@ -425,110 +476,112 @@
     }
 
     /**
-     * Callback used to monitor status of the TV IApp.
+     * Callback used to monitor status of the TV Interactive App.
      * @hide
      */
-    public abstract static class TvIAppCallback {
+    public abstract static class TvInteractiveAppCallback {
         /**
-         * This is called when a TV IApp service is added to the system.
+         * This is called when a TV Interactive App service is added to the system.
          *
-         * <p>Normally it happens when the user installs a new TV IApp service package that
-         * implements {@link TvIAppService} interface.
+         * <p>Normally it happens when the user installs a new TV Interactive App service package
+         * that implements {@link TvIAppService} interface.
          *
-         * @param iAppServiceId The ID of the TV IApp service.
+         * @param iAppServiceId The ID of the TV Interactive App service.
          */
-        public void onIAppServiceAdded(@NonNull String iAppServiceId) {
+        public void onInteractiveAppServiceAdded(@NonNull String iAppServiceId) {
         }
 
         /**
-         * This is called when a TV IApp service is removed from the system.
+         * This is called when a TV Interactive App service is removed from the system.
          *
-         * <p>Normally it happens when the user uninstalls the previously installed TV IApp service
-         * package.
+         * <p>Normally it happens when the user uninstalls the previously installed TV Interactive
+         * App service package.
          *
-         * @param iAppServiceId The ID of the TV IApp service.
+         * @param iAppServiceId The ID of the TV Interactive App service.
          */
-        public void onIAppServiceRemoved(@NonNull String iAppServiceId) {
+        public void onInteractiveAppServiceRemoved(@NonNull String iAppServiceId) {
         }
 
         /**
-         * This is called when a TV IApp service is updated on the system.
+         * This is called when a TV Interactive App service is updated on the system.
          *
-         * <p>Normally it happens when a previously installed TV IApp service package is
+         * <p>Normally it happens when a previously installed TV Interactive App service package is
          * re-installed or a newer version of the package exists becomes available/unavailable.
          *
-         * @param iAppServiceId The ID of the TV IApp service.
+         * @param iAppServiceId The ID of the TV Interactive App service.
          */
-        public void onIAppServiceUpdated(@NonNull String iAppServiceId) {
+        public void onInteractiveAppServiceUpdated(@NonNull String iAppServiceId) {
         }
 
         /**
-         * This is called when the information about an existing TV IApp service has been updated.
+         * This is called when the information about an existing TV Interactive App service has been
+         * updated.
          *
-         * <p>Because the system automatically creates a <code>TvIAppInfo</code> object for each TV
-         * IApp service based on the information collected from the
+         * <p>Because the system automatically creates a <code>TvInteractiveAppInfo</code> object
+         * for each TV Interactive App service based on the information collected from the
          * <code>AndroidManifest.xml</code>, this method is only called back when such information
          * has changed dynamically.
          *
-         * @param iAppInfo The <code>TvIAppInfo</code> object that contains new information.
+         * @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new
+         *                 information.
          */
-        public void onTvIAppInfoUpdated(@NonNull TvIAppInfo iAppInfo) {
+        public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) {
         }
 
         /**
          * This is called when the state of the interactive app service is changed.
          * @hide
          */
-        public void onTvIAppServiceStateChanged(
-                @NonNull String iAppServiceId, int type, @TvIAppRteState int state) {
+        public void onTvInteractiveAppServiceStateChanged(
+                @NonNull String iAppServiceId, int type, @TvInteractiveAppRteState int state) {
         }
     }
 
-    private static final class TvIAppCallbackRecord {
-        private final TvIAppCallback mCallback;
+    private static final class TvInteractiveAppCallbackRecord {
+        private final TvInteractiveAppCallback mCallback;
         private final Handler mHandler;
 
-        TvIAppCallbackRecord(TvIAppCallback callback, Handler handler) {
+        TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Handler handler) {
             mCallback = callback;
             mHandler = handler;
         }
 
-        public TvIAppCallback getCallback() {
+        public TvInteractiveAppCallback getCallback() {
             return mCallback;
         }
 
-        public void postIAppServiceAdded(final String iAppServiceId) {
+        public void postInteractiveAppServiceAdded(final String iAppServiceId) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onIAppServiceAdded(iAppServiceId);
+                    mCallback.onInteractiveAppServiceAdded(iAppServiceId);
                 }
             });
         }
 
-        public void postIAppServiceRemoved(final String iAppServiceId) {
+        public void postInteractiveAppServiceRemoved(final String iAppServiceId) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onIAppServiceRemoved(iAppServiceId);
+                    mCallback.onInteractiveAppServiceRemoved(iAppServiceId);
                 }
             });
         }
 
-        public void postIAppServiceUpdated(final String iAppServiceId) {
+        public void postInteractiveAppServiceUpdated(final String iAppServiceId) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onIAppServiceUpdated(iAppServiceId);
+                    mCallback.onInteractiveAppServiceUpdated(iAppServiceId);
                 }
             });
         }
 
-        public void postTvIAppInfoUpdated(final TvIAppInfo iAppInfo) {
+        public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onTvIAppInfoUpdated(iAppInfo);
+                    mCallback.onTvInteractiveAppInfoUpdated(iAppInfo);
                 }
             });
         }
@@ -537,7 +590,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onTvIAppServiceStateChanged(iAppServiceId, type, state);
+                    mCallback.onTvInteractiveAppServiceStateChanged(iAppServiceId, type, state);
                 }
             });
         }
@@ -578,23 +631,23 @@
     }
 
     /**
-     * Returns the complete list of TV IApp service on the system.
+     * Returns the complete list of TV Interactive App service on the system.
      *
-     * @return List of {@link TvIAppInfo} for each TV IApp service that describes its meta
-     *         information.
+     * @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that
+     *         describes its meta information.
      * @hide
      */
     @NonNull
-    public List<TvIAppInfo> getTvIAppServiceList() {
+    public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() {
         try {
-            return mService.getTvIAppServiceList(mUserId);
+            return mService.getTvInteractiveAppServiceList(mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Prepares TV IApp service for the given type.
+     * Prepares TV Interactive App service for the given type.
      * @hide
      */
     public void prepare(@NonNull String tvIAppServiceId, int type) {
@@ -606,12 +659,25 @@
     }
 
     /**
-     * Notifies app link info.
+     * Registers app link info.
      * @hide
      */
-    public void notifyAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+    public void registerAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
         try {
-            mService.notifyAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+            mService.registerAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters app link info.
+     * @hide
+     */
+    public void unregisterAppLinkInfo(
+            @NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+        try {
+            mService.unregisterAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -630,32 +696,33 @@
     }
 
     /**
-     * Registers a {@link TvIAppManager.TvIAppCallback}.
+     * Registers a {@link TvInteractiveAppCallback}.
      *
-     * @param callback A callback used to monitor status of the TV IApp services.
+     * @param callback A callback used to monitor status of the TV Interactive App services.
      * @param handler A {@link Handler} that the status change will be delivered to.
      * @hide
      */
-    public void registerCallback(@NonNull TvIAppCallback callback, @NonNull Handler handler) {
+    public void registerCallback(
+            @NonNull TvInteractiveAppCallback callback, @NonNull Handler handler) {
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(handler);
         synchronized (mLock) {
-            mCallbackRecords.add(new TvIAppCallbackRecord(callback, handler));
+            mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, handler));
         }
     }
 
     /**
-     * Unregisters the existing {@link TvIAppManager.TvIAppCallback}.
+     * Unregisters the existing {@link TvInteractiveAppCallback}.
      *
      * @param callback The existing callback to remove.
      * @hide
      */
-    public void unregisterCallback(@NonNull final TvIAppCallback callback) {
+    public void unregisterCallback(@NonNull final TvInteractiveAppCallback callback) {
         Preconditions.checkNotNull(callback);
         synchronized (mLock) {
-            for (Iterator<TvIAppCallbackRecord> it = mCallbackRecords.iterator();
+            for (Iterator<TvInteractiveAppCallbackRecord> it = mCallbackRecords.iterator();
                     it.hasNext(); ) {
-                TvIAppCallbackRecord record = it.next();
+                TvInteractiveAppCallbackRecord record = it.next();
                 if (record.getCallback() == callback) {
                     it.remove();
                     break;
@@ -692,8 +759,8 @@
         private TvInputEventSender mSender;
         private InputChannel mInputChannel;
 
-        private Session(IBinder token, InputChannel channel, ITvIAppManager service, int userId,
-                int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
+        private Session(IBinder token, InputChannel channel, ITvIAppManager service,
+                int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
             mToken = token;
             mInputChannel = channel;
             mService = service;
@@ -710,25 +777,37 @@
             mInputSession = inputSession;
         }
 
-        void startIApp() {
+        void startInteractiveApp() {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.startIApp(mToken, mUserId);
+                mService.startInteractiveApp(mToken, mUserId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
 
-        void stopIApp() {
+        void stopInteractiveApp() {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.stopIApp(mToken, mUserId);
+                mService.stopInteractiveApp(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        void resetInteractiveApp() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.resetInteractiveApp(mToken, mUserId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -758,6 +837,18 @@
             }
         }
 
+        void setTeletextAppEnabled(boolean enable) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.setTeletextAppEnabled(mToken, enable, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         void sendCurrentChannelUri(@Nullable Uri channelUri) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
@@ -806,6 +897,18 @@
             }
         }
 
+        void sendCurrentTvInputId(@Nullable String inputId) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.sendCurrentTvInputId(mToken, inputId, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         /**
          * Sets the {@link android.view.Surface} for this session.
          *
@@ -994,7 +1097,7 @@
         }
 
         /**
-         * Notifies IAPP session when a channel is tuned.
+         * Notifies Interactive APP session when a channel is tuned.
          */
         public void notifyTuned(Uri channelUri) {
             if (mToken == null) {
@@ -1009,7 +1112,7 @@
         }
 
         /**
-         * Notifies IAPP session when a track is selected.
+         * Notifies Interactive APP session when a track is selected.
          */
         public void notifyTrackSelected(int type, String trackId) {
             if (mToken == null) {
@@ -1024,7 +1127,7 @@
         }
 
         /**
-         * Notifies IAPP session when tracks are changed.
+         * Notifies Interactive APP session when tracks are changed.
          */
         public void notifyTracksChanged(List<TvTrackInfo> tracks) {
             if (mToken == null) {
@@ -1098,6 +1201,21 @@
             }
         }
 
+        /**
+         * Notifies Interactive APP session when signal strength is changed.
+         */
+        public void notifySignalStrength(int strength) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifySignalStrength(mToken, strength, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         private void flushPendingEventsLocked() {
             mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
 
@@ -1359,7 +1477,8 @@
             });
         }
 
-        void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType,
+        void postCommandRequest(
+                final @TvIAppService.InteractiveAppServiceCommandType String cmdType,
                 final Bundle parameters) {
             mHandler.post(new Runnable() {
                 @Override
@@ -1414,6 +1533,15 @@
             });
         }
 
+        void postRequestCurrentTvInputId() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRequestCurrentTvInputId(mSession);
+                }
+            });
+        }
+
         void postAdRequest(final AdRequest request) {
             mHandler.post(new Runnable() {
                 @Override
@@ -1442,6 +1570,15 @@
                 }
             });
         }
+
+        void postTeletextAppStateChanged(int state) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onTeletextAppStateChanged(mSession, state);
+                }
+            });
+        }
     }
 
     /**
@@ -1452,8 +1589,8 @@
         /**
          * This is called after {@link TvIAppManager#createSession} has been processed.
          *
-         * @param session A {@link TvIAppManager.Session} instance created. This can be {@code null}
-         *                if the creation request failed.
+         * @param session A {@link TvIAppManager.Session} instance created. This can be
+         *                {@code null} if the creation request failed.
          */
         public void onSessionCreated(@Nullable Session session) {
         }
@@ -1468,8 +1605,8 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#layoutSurface} is called to change the
-         * layout of surface.
+         * This is called when {@link TvIAppService.Session#layoutSurface} is called to
+         * change the layout of surface.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
          * @param left Left position.
@@ -1487,8 +1624,10 @@
          * @param cmdType type of the command.
          * @param parameters parameters of the command.
          */
-        public void onCommandRequest(Session session,
-                @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+        public void onCommandRequest(
+                Session session,
+                @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                Bundle parameters) {
         }
 
         /**
@@ -1500,7 +1639,8 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is called.
+         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+         * called.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
          */
@@ -1508,7 +1648,8 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is called.
+         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+         * called.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
          */
@@ -1516,7 +1657,8 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is called.
+         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+         * called.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
          */
@@ -1524,7 +1666,8 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is called.
+         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+         * called.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
          */
@@ -1532,6 +1675,15 @@
         }
 
         /**
+         * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
+         *
+         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @hide
+         */
+        public void onRequestCurrentTvInputId(Session session) {
+        }
+
+        /**
          * This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
@@ -1541,8 +1693,8 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated} is
-         * called.
+         * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated}
+         * is called.
          *
          * @param session A {@link TvIAppManager.Session} associated with this callback.
          * @param biIAppUri URI associated this BI interactive app. This is the same URI in
@@ -1552,5 +1704,16 @@
          */
         public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
         }
+
+        /**
+         * This is called when {@link TvIAppService.Session#notifyTeletextAppStateChanged} is
+         * called.
+         *
+         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param state the current state.
+         */
+        public void onTeletextAppStateChanged(
+                Session session, @TvIAppManager.TeletextAppState int state) {
+        }
     }
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
old mode 100644
new mode 100755
index 4993bc3..c0ec76b
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -32,6 +32,7 @@
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvContentRating;
+import android.media.tv.TvInputManager;
 import android.media.tv.TvTrackInfo;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -75,45 +76,48 @@
     // TODO: cleanup and unhide APIs.
 
     /**
-     * This is the interface name that a service implementing a TV IApp service should say that it
-     * supports -- that is, this is the action it uses for its intent filter. To be supported, the
-     * service must also require the android.Manifest.permission#BIND_TV_IAPP permission so
-     * that other applications cannot abuse it.
+     * This is the interface name that a service implementing a TV Interactive App service should
+     * say that it supports -- that is, this is the action it uses for its intent filter. To be
+     * supported, the service must also require the
+     * android.Manifest.permission#BIND_TV_INTERACTIVE_APP permission so that other applications
+     * cannot abuse it.
      */
-    public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
+    public static final String SERVICE_INTERFACE =
+            "android.media.tv.interactive.TvIAppService";
 
     /**
-     * Name under which a TvIAppService component publishes information about itself. This meta-data
-     * must reference an XML resource containing an
-     * <code>&lt;{@link android.R.styleable#TvIAppService tv-iapp}&gt;</code>
+     * Name under which a TvIAppService component publishes information about itself. This
+     * meta-data must reference an XML resource containing an
+     * <code>&lt;{@link android.R.styleable#TvIAppService tv-interactive-app}&gt;</code>
      * tag.
      */
     public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = {
-            IAPP_SERVICE_COMMAND_TYPE_TUNE,
-            IAPP_SERVICE_COMMAND_TYPE_TUNE_NEXT,
-            IAPP_SERVICE_COMMAND_TYPE_TUNE_PREV,
-            IAPP_SERVICE_COMMAND_TYPE_STOP,
-            IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME,
-            IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK
+    @StringDef(prefix = "INTERACTIVE_APP_SERVICE_COMMAND_TYPE_", value = {
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_NEXT,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_PREV,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_STOP,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME,
+            INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SELECT_TRACK
     })
-    public @interface IAppServiceCommandType {}
+    public @interface InteractiveAppServiceCommandType {}
 
     /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "tune";
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE = "tune";
     /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE_NEXT = "tune_next";
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_NEXT = "tune_next";
     /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE_PREV = "tune_previous";
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_TUNE_PREV = "tune_previous";
     /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "stop";
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_STOP = "stop";
     /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME = "set_stream_volume";
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME =
+            "set_stream_volume";
     /** @hide */
-    public static final String IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track";
+    public static final String INTERACTIVE_APP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track";
     /** @hide */
     public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri";
     /** @hide */
@@ -129,29 +133,29 @@
             "command_track_select_mode";
 
     private final Handler mServiceHandler = new ServiceHandler();
-    private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
+    private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
             new RemoteCallbackList<>();
 
     /** @hide */
     @Override
     public final IBinder onBind(Intent intent) {
-        ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
+        ITvInteractiveAppService.Stub tvIAppServiceBinder = new ITvInteractiveAppService.Stub() {
             @Override
-            public void registerCallback(ITvIAppServiceCallback cb) {
+            public void registerCallback(ITvInteractiveAppServiceCallback cb) {
                 if (cb != null) {
                     mCallbacks.register(cb);
                 }
             }
 
             @Override
-            public void unregisterCallback(ITvIAppServiceCallback cb) {
+            public void unregisterCallback(ITvInteractiveAppServiceCallback cb) {
                 if (cb != null) {
                     mCallbacks.unregister(cb);
                 }
             }
 
             @Override
-            public void createSession(InputChannel channel, ITvIAppSessionCallback cb,
+            public void createSession(InputChannel channel, ITvInteractiveAppSessionCallback cb,
                     String iAppServiceId, int type) {
                 if (cb == null) {
                     return;
@@ -171,8 +175,13 @@
             }
 
             @Override
-            public void notifyAppLinkInfo(Bundle appLinkInfo) {
-                onAppLinkInfo(appLinkInfo);
+            public void registerAppLinkInfo(Bundle appLinkInfo) {
+                onRegisterAppLinkInfo(appLinkInfo);
+            }
+
+            @Override
+            public void unregisterAppLinkInfo(Bundle appLinkInfo) {
+                onUnregisterAppLinkInfo(appLinkInfo);
             }
 
             @Override
@@ -184,7 +193,7 @@
     }
 
     /**
-     * Prepares TV IApp service for the given type.
+     * Prepares TV Interactive App service for the given type.
      * @hide
      */
     public void onPrepare(int type) {
@@ -195,7 +204,15 @@
      * Registers App link info.
      * @hide
      */
-    public void onAppLinkInfo(Bundle appLinkInfo) {
+    public void onRegisterAppLinkInfo(Bundle appLinkInfo) {
+        // TODO: make it abstract when unhide
+    }
+
+    /**
+     * Unregisters App link info.
+     * @hide
+     */
+    public void onUnregisterAppLinkInfo(Bundle appLinkInfo) {
         // TODO: make it abstract when unhide
     }
 
@@ -211,11 +228,11 @@
     /**
      * Returns a concrete implementation of {@link Session}.
      *
-     * <p>May return {@code null} if this TV IApp service fails to create a session for some
-     * reason.
+     * <p>May return {@code null} if this TV Interactive App service fails to create a session for
+     * some reason.
      *
-     * @param iAppServiceId The ID of the TV IApp associated with the session.
-     * @param type The type of the TV IApp associated with the session.
+     * @param iAppServiceId The ID of the TV Interactive App associated with the session.
+     * @param type The type of the TV Interactive App associated with the session.
      * @hide
      */
     @Nullable
@@ -229,7 +246,8 @@
      * @param state the current state
      * @hide
      */
-    public final void notifyStateChanged(int type, @TvIAppManager.TvIAppRteState int state) {
+    public final void notifyStateChanged(
+            int type, @TvIAppManager.TvInteractiveAppRteState int state) {
         mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED,
                 type, state).sendToTarget();
     }
@@ -243,7 +261,7 @@
 
         private final Object mLock = new Object();
         // @GuardedBy("mLock")
-        private ITvIAppSessionCallback mSessionCallback;
+        private ITvInteractiveAppSessionCallback mSessionCallback;
         // @GuardedBy("mLock")
         private final List<Runnable> mPendingActions = new ArrayList<>();
 
@@ -276,7 +294,7 @@
          * <p>By default, the media view is disabled. Must be called explicitly after the
          * session is created to enable the media view.
          *
-         * <p>The TV IApp service can disable its media view when needed.
+         * <p>The TV Interactive App service can disable its media view when needed.
          *
          * @param enable {@code true} if you want to enable the media view. {@code false}
          *            otherwise.
@@ -304,14 +322,21 @@
          * Starts TvIAppService session.
          * @hide
          */
-        public void onStartIApp() {
+        public void onStartInteractiveApp() {
         }
 
         /**
          * Stops TvIAppService session.
          * @hide
          */
-        public void onStopIApp() {
+        public void onStopInteractiveApp() {
+        }
+
+        /**
+         * Resets TvIAppService session.
+         * @hide
+         */
+        public void onResetInteractiveApp() {
         }
 
         /**
@@ -337,6 +362,13 @@
         }
 
         /**
+         * To toggle Digital Teletext Application if there is one in AIT app list.
+         * @param enable
+         */
+        public void onSetTeletextAppEnabled(boolean enable) {
+        }
+
+        /**
          * Receives current channel URI.
          * @hide
          */
@@ -365,11 +397,18 @@
         }
 
         /**
+         * Receives current TV input ID.
+         * @hide
+         */
+        public void onCurrentTvInputId(@Nullable String inputId) {
+        }
+
+        /**
          * Called when the application sets the surface.
          *
-         * <p>The TV IApp service should render interactive app UI onto the given surface. When
-         * called with {@code null}, the IApp service should immediately free any references to the
-         * currently set surface and stop using it.
+         * <p>The TV Interactive App service should render interactive app UI onto the given
+         * surface. When called with {@code null}, the Interactive App service should immediately
+         * free any references to the currently set surface and stop using it.
          *
          * @param surface The surface to be used for interactive app UI rendering. Can be
          *                {@code null}.
@@ -394,8 +433,8 @@
          *
          * <p>This is always called at least once when the session is created regardless of whether
          * the media view is enabled or not. The media view container size is the same as the
-         * containing {@link TvIAppView}. Note that the size of the underlying surface can be
-         * different if the surface was changed by calling {@link #layoutSurface}.
+         * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can
+         * be different if the surface was changed by calling {@link #layoutSurface}.
          *
          * @param width The width of the media view.
          * @param height The height of the media view.
@@ -471,6 +510,13 @@
         }
 
         /**
+         * Called when signal strength is changed.
+         * @hide
+         */
+        public void onSignalStrength(@TvInputManager.SignalStrength int strength) {
+        }
+
+        /**
          * Called when a broadcast info response is received.
          * @hide
          */
@@ -624,7 +670,8 @@
          * @param cmdType type of the specific command
          * @param parameters parameters of the specific command
          */
-        public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) {
+        public void requestCommand(
+                @InteractiveAppServiceCommandType String cmdType, Bundle parameters) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -755,6 +802,31 @@
         }
 
         /**
+         * Requests current TV input ID.
+         *
+         * @see android.media.tv.TvInputInfo
+         * @hide
+         */
+        public void requestCurrentTvInputId() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "requestCurrentTvInputId");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRequestCurrentTvInputId();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in requestCurrentTvInputId", e);
+                    }
+                }
+            });
+        }
+
+        /**
          * requests an advertisement request to be processed by the related TV input.
          * @param request advertisement request
          */
@@ -777,12 +849,16 @@
             });
         }
 
-        void startIApp() {
-            onStartIApp();
+        void startInteractiveApp() {
+            onStartInteractiveApp();
         }
 
-        void stopIApp() {
-            onStopIApp();
+        void stopInteractiveApp() {
+            onStopInteractiveApp();
+        }
+
+        void resetInteractiveApp() {
+            onResetInteractiveApp();
         }
 
         void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
@@ -793,6 +869,10 @@
             onDestroyBiInteractiveApp(biIAppId);
         }
 
+        void setTeletextAppEnabled(boolean enable) {
+            onSetTeletextAppEnabled(enable);
+        }
+
         void sendCurrentChannelUri(@Nullable Uri channelUri) {
             onCurrentChannelUri(channelUri);
         }
@@ -809,6 +889,10 @@
             onTrackInfoList(tracks);
         }
 
+        void sendCurrentTvInputId(@Nullable String inputId) {
+            onCurrentTvInputId(inputId);
+        }
+
         void release() {
             onRelease();
             if (mSurface != null) {
@@ -873,6 +957,13 @@
             onContentBlocked(rating);
         }
 
+        void notifySignalStrength(int strength) {
+            if (DEBUG) {
+                Log.d(TAG, "notifySignalStrength (strength=" + strength + ")");
+            }
+            onSignalStrength(strength);
+        }
+
         /**
          * Calls {@link #onBroadcastInfoResponse}.
          */
@@ -898,7 +989,8 @@
          * Notifies when the session state is changed.
          * @param state the current state.
          */
-        public void notifySessionStateChanged(@TvIAppManager.TvIAppRteState int state) {
+        public void notifySessionStateChanged(
+                @TvIAppManager.TvInteractiveAppRteState int state) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -945,6 +1037,30 @@
         }
 
         /**
+         * Notifies when the digital teletext app state is changed.
+         * @param state the current state.
+         */
+        public final void notifyTeletextAppStateChanged(@TvIAppManager.TeletextAppState int state) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG, "notifyTeletextAppState (state="
+                                    + state + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onTeletextAppStateChanged(state);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyTeletextAppState", e);
+                    }
+                }
+            });
+        }
+
+        /**
          * Takes care of dispatching incoming input events and tells whether the event was handled.
          */
         int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
@@ -977,7 +1093,7 @@
             return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
         }
 
-        private void initialize(ITvIAppSessionCallback callback) {
+        private void initialize(ITvInteractiveAppSessionCallback callback) {
             synchronized (mLock) {
                 mSessionCallback = callback;
                 for (Runnable runnable : mPendingActions) {
@@ -1087,8 +1203,8 @@
             if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")");
             if (mMediaFrame == null || mMediaFrame.width() != frame.width()
                     || mMediaFrame.height() != frame.height()) {
-                // Note: relayoutMediaView is called whenever TvIAppView's layout is changed
-                // regardless of setMediaViewEnabled.
+                // Note: relayoutMediaView is called whenever TvInteractiveAppView's layout is
+                // changed regardless of setMediaViewEnabled.
                 onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
             }
             mMediaFrame = frame;
@@ -1159,31 +1275,37 @@
     }
 
     /**
-     * Implements the internal ITvIAppSession interface.
+     * Implements the internal ITvInteractiveAppSession interface.
      * @hide
      */
-    public static class ITvIAppSessionWrapper extends ITvIAppSession.Stub {
-        // TODO: put ITvIAppSessionWrapper in a separate Java file
+    public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub {
+        // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file
         private final Session mSessionImpl;
         private InputChannel mChannel;
-        private TvIAppEventReceiver mReceiver;
+        private TvInteractiveAppEventReceiver mReceiver;
 
-        public ITvIAppSessionWrapper(Context context, Session mSessionImpl, InputChannel channel) {
+        public ITvInteractiveAppSessionWrapper(
+                Context context, Session mSessionImpl, InputChannel channel) {
             this.mSessionImpl = mSessionImpl;
             mChannel = channel;
             if (channel != null) {
-                mReceiver = new TvIAppEventReceiver(channel, context.getMainLooper());
+                mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
             }
         }
 
         @Override
-        public void startIApp() {
-            mSessionImpl.startIApp();
+        public void startInteractiveApp() {
+            mSessionImpl.startInteractiveApp();
         }
 
         @Override
-        public void stopIApp() {
-            mSessionImpl.stopIApp();
+        public void stopInteractiveApp() {
+            mSessionImpl.stopInteractiveApp();
+        }
+
+        @Override
+        public void resetInteractiveApp() {
+            mSessionImpl.resetInteractiveApp();
         }
 
         @Override
@@ -1192,6 +1314,11 @@
         }
 
         @Override
+        public void setTeletextAppEnabled(boolean enable) {
+            mSessionImpl.setTeletextAppEnabled(enable);
+        }
+
+        @Override
         public void destroyBiInteractiveApp(@NonNull String biIAppId) {
             mSessionImpl.destroyBiInteractiveApp(biIAppId);
         }
@@ -1217,6 +1344,11 @@
         }
 
         @Override
+        public void sendCurrentTvInputId(@Nullable String inputId) {
+            mSessionImpl.sendCurrentTvInputId(inputId);
+        }
+
+        @Override
         public void release() {
             mSessionImpl.scheduleMediaViewCleanup();
             mSessionImpl.release();
@@ -1258,6 +1390,11 @@
         }
 
         @Override
+        public void notifySignalStrength(int strength) {
+            mSessionImpl.notifySignalStrength(strength);
+        }
+
+        @Override
         public void setSurface(Surface surface) {
             mSessionImpl.setSurface(surface);
         }
@@ -1292,8 +1429,8 @@
             mSessionImpl.removeMediaView(true);
         }
 
-        private final class TvIAppEventReceiver extends InputEventReceiver {
-            TvIAppEventReceiver(InputChannel inputChannel, Looper looper) {
+        private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
+            TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
                 super(inputChannel, looper);
             }
 
@@ -1307,7 +1444,8 @@
 
                 int handled = mSessionImpl.dispatchInputEvent(event, this);
                 if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
-                    finishInputEvent(event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
+                    finishInputEvent(
+                            event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
                 }
             }
         }
@@ -1337,7 +1475,8 @@
                 case DO_CREATE_SESSION: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     InputChannel channel = (InputChannel) args.arg1;
-                    ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg2;
+                    ITvInteractiveAppSessionCallback cb =
+                            (ITvInteractiveAppSessionCallback) args.arg2;
                     String iAppServiceId = (String) args.arg3;
                     int type = (int) args.arg4;
                     args.recycle();
@@ -1351,8 +1490,8 @@
                         }
                         return;
                     }
-                    ITvIAppSession stub = new ITvIAppSessionWrapper(
-                            TvIAppService.this, sessionImpl, channel);
+                    ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper(
+                            android.media.tv.interactive.TvIAppService.this, sessionImpl, channel);
 
                     SomeArgs someArgs = SomeArgs.obtain();
                     someArgs.arg1 = sessionImpl;
@@ -1365,8 +1504,9 @@
                 case DO_NOTIFY_SESSION_CREATED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     Session sessionImpl = (Session) args.arg1;
-                    ITvIAppSession stub = (ITvIAppSession) args.arg2;
-                    ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg3;
+                    ITvInteractiveAppSession stub = (ITvInteractiveAppSession) args.arg2;
+                    ITvInteractiveAppSessionCallback cb =
+                            (ITvInteractiveAppSessionCallback) args.arg3;
                     try {
                         cb.onSessionCreated(stub);
                     } catch (RemoteException e) {
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
similarity index 95%
rename from media/java/android/media/tv/interactive/TvIAppInfo.aidl
rename to media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
index 6041460..5e15016 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.media.tv.interactive;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable TvInteractiveAppInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
new file mode 100644
index 0000000..2f96552
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV interactive app.
+ * @hide
+ */
+public final class TvInteractiveAppInfo implements Parcelable {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvInteractiveAppInfo";
+
+    private static final String XML_START_TAG_NAME = "tv-interactive-app";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
+            INTERACTIVE_APP_TYPE_HBBTV,
+            INTERACTIVE_APP_TYPE_ATSC,
+            INTERACTIVE_APP_TYPE_GINGA,
+    })
+    @interface InteractiveAppType {}
+
+    /** HbbTV interactive app type */
+    public static final int INTERACTIVE_APP_TYPE_HBBTV = 0x1;
+    /** ATSC interactive app type */
+    public static final int INTERACTIVE_APP_TYPE_ATSC = 0x2;
+    /** Ginga interactive app type */
+    public static final int INTERACTIVE_APP_TYPE_GINGA = 0x4;
+
+    private final ResolveInfo mService;
+    private final String mId;
+    private int mTypes;
+
+    public TvInteractiveAppInfo(@NonNull Context context, @NonNull ComponentName component) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null.");
+        }
+        Intent intent =
+                new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+        ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        if (resolveInfo == null) {
+            throw new IllegalArgumentException("Invalid component. Can't find the service.");
+        }
+
+        ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+                resolveInfo.serviceInfo.name);
+        String id;
+        id = generateInteractiveAppServiceId(componentName);
+        List<String> types = new ArrayList<>();
+        parseServiceMetadata(resolveInfo, context, types);
+
+        mService = resolveInfo;
+        mId = id;
+        mTypes = toTypesFlag(types);
+    }
+    private TvInteractiveAppInfo(ResolveInfo service, String id, int types) {
+        mService = service;
+        mId = id;
+        mTypes = types;
+    }
+
+    private TvInteractiveAppInfo(@NonNull Parcel in) {
+        mService = ResolveInfo.CREATOR.createFromParcel(in);
+        mId = in.readString();
+        mTypes = in.readInt();
+    }
+
+    public static final @NonNull Creator<TvInteractiveAppInfo> CREATOR =
+            new Creator<TvInteractiveAppInfo>() {
+                @Override
+                public TvInteractiveAppInfo createFromParcel(Parcel in) {
+                    return new TvInteractiveAppInfo(in);
+                }
+
+                @Override
+                public TvInteractiveAppInfo[] newArray(int size) {
+                    return new TvInteractiveAppInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mId);
+        dest.writeInt(mTypes);
+    }
+
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the component of the TV Interactive App service.
+     * @hide
+     */
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+    }
+
+    /**
+     * Returns the information of the service that implements this TV Interactive App service.
+     */
+    @Nullable
+    public ServiceInfo getServiceInfo() {
+        return mService.serviceInfo;
+    }
+
+    /**
+     * Gets supported interactive app types
+     */
+    @InteractiveAppType
+    @NonNull
+    public int getSupportedTypes() {
+        return mTypes;
+    }
+
+    private static String generateInteractiveAppServiceId(ComponentName name) {
+        return name.flattenToShortString();
+    }
+
+    private static void parseServiceMetadata(
+            ResolveInfo resolveInfo, Context context, List<String> types) {
+        ServiceInfo si = resolveInfo.serviceInfo;
+        PackageManager pm = context.getPackageManager();
+        try (XmlResourceParser parser =
+                     si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
+            if (parser == null) {
+                throw new IllegalStateException(
+                        "No " + TvIAppService.SERVICE_META_DATA
+                        + " meta-data found for " + si.name);
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // move to the START_TAG
+            }
+
+            String nodeName = parser.getName();
+            if (!XML_START_TAG_NAME.equals(nodeName)) {
+                throw new IllegalStateException("Meta-data does not start with "
+                        + XML_START_TAG_NAME + " tag for " + si.name);
+            }
+
+            TypedArray sa = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.TvIAppService);
+            CharSequence[] textArr = sa.getTextArray(
+                    com.android.internal.R.styleable.TvIAppService_supportedTypes);
+            for (CharSequence cs : textArr) {
+                types.add(cs.toString().toLowerCase());
+            }
+
+            sa.recycle();
+        } catch (IOException | XmlPullParserException e) {
+            throw new IllegalStateException(
+                    "Failed reading meta-data for " + si.packageName, e);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("No resources found for " + si.packageName, e);
+        }
+    }
+
+    private static int toTypesFlag(List<String> types) {
+        int flag = 0;
+        for (String type : types) {
+            switch (type) {
+                case "hbbtv":
+                    flag |= INTERACTIVE_APP_TYPE_HBBTV;
+                    break;
+                case "atsc":
+                    flag |= INTERACTIVE_APP_TYPE_ATSC;
+                    break;
+                case "ginga":
+                    flag |= INTERACTIVE_APP_TYPE_GINGA;
+                    break;
+                default:
+                    break;
+            }
+        }
+        return flag;
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
old mode 100644
new mode 100755
similarity index 72%
rename from media/java/android/media/tv/interactive/TvIAppView.java
rename to media/java/android/media/tv/interactive/TvInteractiveAppView.java
index b295055..6f99d51
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.interactive;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -45,13 +46,14 @@
 import android.view.ViewRootImpl;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Displays contents of interactive TV applications.
  * @hide
  */
-public class TvIAppView extends ViewGroup {
-    private static final String TAG = "TvIAppView";
+public class TvInteractiveAppView extends ViewGroup {
+    private static final String TAG = "TvInteractiveAppView";
     private static final boolean DEBUG = false;
 
     private static final int SET_TVVIEW_SUCCESS = 1;
@@ -59,11 +61,13 @@
     private static final int UNSET_TVVIEW_SUCCESS = 3;
     private static final int UNSET_TVVIEW_FAIL = 4;
 
-    private final TvIAppManager mTvIAppManager;
+    private final TvIAppManager mTvInteractiveAppManager;
     private final Handler mHandler = new Handler();
+    private final Object mCallbackLock = new Object();
     private Session mSession;
     private MySessionCallback mSessionCallback;
-    private TvIAppCallback mCallback;
+    private TvInteractiveAppCallback mCallback;
+    private Executor mCallbackExecutor;
     private SurfaceView mSurfaceView;
     private Surface mSurface;
 
@@ -114,15 +118,16 @@
         }
     };
 
-    public TvIAppView(@NonNull Context context) {
+    public TvInteractiveAppView(@NonNull Context context) {
         this(context, null, 0);
     }
 
-    public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
+    public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         int sourceResId = Resources.getAttributeSetSourceResId(attrs);
         if (sourceResId != Resources.ID_NULL) {
@@ -136,31 +141,50 @@
         }
         mDefStyleAttr = defStyleAttr;
         resetSurfaceView();
-        mTvIAppManager = (TvIAppManager) getContext().getSystemService(Context.TV_IAPP_SERVICE);
+        mTvInteractiveAppManager = (TvIAppManager) getContext().getSystemService(
+                Context.TV_IAPP_SERVICE);
     }
 
     /**
-     * Sets the callback to be invoked when an event is dispatched to this TvIAppView.
+     * Sets the callback to be invoked when an event is dispatched to this TvInteractiveAppView.
      *
      * @param callback The callback to receive events. A value of {@code null} removes the existing
-     *            callback.
+     *                 callback.
      */
-    public void setCallback(@Nullable TvIAppCallback callback) {
-        mCallback = callback;
+    public void setCallback(
+            @NonNull TvInteractiveAppCallback callback,
+            @NonNull @CallbackExecutor Executor executor) {
+        synchronized (mCallbackLock) {
+            mCallbackExecutor = executor;
+            mCallback = callback;
+        }
     }
 
+    /**
+     * Clears the callback.
+     */
+    public void clearCallback() {
+        synchronized (mCallbackLock) {
+            mCallback = null;
+            mCallbackExecutor = null;
+        }
+    }
+
+    /** @hide */
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         createSessionMediaView();
     }
 
+    /** @hide */
     @Override
     protected void onDetachedFromWindow() {
         removeSessionMediaView();
         super.onDetachedFromWindow();
     }
 
+    /** @hide */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (DEBUG) {
@@ -175,6 +199,7 @@
         }
     }
 
+    /** @hide */
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
@@ -186,6 +211,7 @@
                         childState << MEASURED_HEIGHT_STATE_SHIFT));
     }
 
+    /** @hide */
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -216,7 +242,8 @@
     }
 
     /**
-     * Resets this TvIAppView.
+     * Resets this TvInteractiveAppView.
+     * @hide
      */
     public void reset() {
         if (DEBUG) Log.d(TAG, "reset()");
@@ -302,6 +329,7 @@
 
     /**
      * Dispatches an unhandled input event to the next receiver.
+     * @hide
      */
     public boolean dispatchUnhandledInputEvent(@NonNull InputEvent event) {
         if (mOnUnhandledInputEventListener != null) {
@@ -314,11 +342,13 @@
 
     /**
      * Called when an unhandled input event also has not been handled by the user provided
-     * callback. This is the last chance to handle the unhandled input event in the TvIAppView.
+     * callback. This is the last chance to handle the unhandled input event in the
+     * TvInteractiveAppView.
      *
      * @param event The input event.
      * @return If you handled the event, return {@code true}. If you want to allow the event to be
      *         handled by the next receiver, return {@code false}.
+     * @hide
      */
     public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
         return false;
@@ -329,6 +359,7 @@
      * by the TV Interactive App.
      *
      * @param listener The callback to be invoked when the unhandled input event is received.
+     * @hide
      */
     public void setOnUnhandledInputEventListener(@NonNull OnUnhandledInputEventListener listener) {
         mOnUnhandledInputEventListener = listener;
@@ -350,44 +381,60 @@
 
     /**
      * Prepares the interactive application.
+     * @hide
      */
-    public void prepareIApp(@NonNull String iAppServiceId, int type) {
+    public void prepareInteractiveApp(@NonNull String iAppServiceId, int type) {
         // TODO: document and handle the cases that this method is called multiple times.
         if (DEBUG) {
-            Log.d(TAG, "prepareIApp");
+            Log.d(TAG, "prepareInteractiveApp");
         }
         mSessionCallback = new MySessionCallback(iAppServiceId, type);
-        if (mTvIAppManager != null) {
-            mTvIAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
+        if (mTvInteractiveAppManager != null) {
+            mTvInteractiveAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
         }
     }
 
     /**
      * Starts the interactive application.
      */
-    public void startIApp() {
+    public void startInteractiveApp() {
         if (DEBUG) {
-            Log.d(TAG, "startIApp");
+            Log.d(TAG, "startInteractiveApp");
         }
         if (mSession != null) {
-            mSession.startIApp();
+            mSession.startInteractiveApp();
         }
     }
 
     /**
      * Stops the interactive application.
+     * @hide
      */
-    public void stopIApp() {
+    public void stopInteractiveApp() {
         if (DEBUG) {
-            Log.d(TAG, "stopIApp");
+            Log.d(TAG, "stopInteractiveApp");
         }
         if (mSession != null) {
-            mSession.stopIApp();
+            mSession.stopInteractiveApp();
+        }
+    }
+
+    /**
+     * Resets the interactive application.
+     * @hide
+     */
+    public void resetInteractiveApp() {
+        if (DEBUG) {
+            Log.d(TAG, "resetInteractiveApp");
+        }
+        if (mSession != null) {
+            mSession.resetInteractiveApp();
         }
     }
 
     /**
      * Sends current channel URI to related TV interactive app.
+     * @hide
      */
     public void sendCurrentChannelUri(Uri channelUri) {
         if (DEBUG) {
@@ -400,6 +447,7 @@
 
     /**
      * Sends current channel logical channel number (LCN) to related TV interactive app.
+     * @hide
      */
     public void sendCurrentChannelLcn(int lcn) {
         if (DEBUG) {
@@ -412,6 +460,7 @@
 
     /**
      * Sends stream volume to related TV interactive app.
+     * @hide
      */
     public void sendStreamVolume(float volume) {
         if (DEBUG) {
@@ -424,6 +473,7 @@
 
     /**
      * Sends track info list to related TV interactive app.
+     * @hide
      */
     public void sendTrackInfoList(List<TvTrackInfo> tracks) {
         if (DEBUG) {
@@ -434,6 +484,23 @@
         }
     }
 
+    /**
+     * Sends current TV input ID to related TV interactive app.
+     *
+     * @param inputId The current TV input ID whose channel is tuned. {@code null} if no channel is
+     *                tuned.
+     * @see android.media.tv.TvInputInfo
+     * @hide
+     */
+    public void sendCurrentTvInputId(@Nullable String inputId) {
+        if (DEBUG) {
+            Log.d(TAG, "sendCurrentTvInputId");
+        }
+        if (mSession != null) {
+            mSession.sendCurrentTvInputId(inputId);
+        }
+    }
+
     private void resetInternal() {
         mSessionCallback = null;
         if (mSession != null) {
@@ -478,16 +545,18 @@
         }
     }
 
-    public Session getIAppSession() {
+    /** @hide */
+    public Session getInteractiveAppSession() {
         return mSession;
     }
 
     /**
-     * Sets the TvIAppView to receive events from TIS. This method links the session of
+     * Sets the TvInteractiveAppView to receive events from TIS. This method links the session of
      * TvIAppManager to TvInputManager session, so the TIAS can get the TIS events.
      *
-     * @param tvView the TvView to be linked to this TvIAppView via linking of Sessions.
+     * @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
      * @return to be added
+     * @hide
      */
     public int setTvView(@Nullable TvView tvView) {
         if (tvView == null) {
@@ -498,7 +567,7 @@
             return SET_TVVIEW_FAIL;
         }
         mSession.setInputSession(inputSession);
-        inputSession.setIAppSession(mSession);
+        inputSession.setInteractiveAppSession(mSession);
         return SET_TVVIEW_SUCCESS;
     }
 
@@ -506,15 +575,29 @@
         if (mSession == null || mSession.getInputSession() == null) {
             return UNSET_TVVIEW_FAIL;
         }
-        mSession.getInputSession().setIAppSession(null);
+        mSession.getInputSession().setInteractiveAppSession(null);
         mSession.setInputSession(null);
         return UNSET_TVVIEW_SUCCESS;
     }
 
     /**
-     * Callback used to receive various status updates on the {@link TvIAppView}.
+     * To toggle Digital Teletext Application if there is one in AIT app list.
+     * @param enable
      */
-    public abstract static class TvIAppCallback {
+    public void setTeletextAppEnabled(boolean enable) {
+        if (DEBUG) {
+            Log.d(TAG, "setTeletextAppEnabled enable=" + enable);
+        }
+        if (mSession != null) {
+            mSession.setTeletextAppEnabled(enable);
+        }
+    }
+
+    /**
+     * Callback used to receive various status updates on the {@link TvInteractiveAppView}.
+     */
+    public abstract static class TvInteractiveAppCallback {
+        // TODO: unhide the following public APIs
 
         /**
          * This is called when a command is requested to be processed by the related TV input.
@@ -522,10 +605,11 @@
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param cmdType type of the command
          * @param parameters parameters of the command
+         * @hide
          */
         public void onCommandRequest(
                 @NonNull String iAppServiceId,
-                @NonNull @TvIAppService.IAppServiceCommandType String cmdType,
+                @NonNull @TvIAppService.InteractiveAppServiceCommandType String cmdType,
                 @Nullable Bundle parameters) {
         }
 
@@ -534,6 +618,7 @@
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param state current session state.
+         * @hide
          */
         public void onSessionStateChanged(@NonNull String iAppServiceId, int state) {
         }
@@ -546,55 +631,84 @@
          *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
          * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
          *                 app.
+         * @hide
          */
         public void onBiInteractiveAppCreated(@NonNull String iAppServiceId, @NonNull Uri biIAppUri,
                 @Nullable String biIAppId) {
         }
 
         /**
+         * This is called when the digital teletext app state is changed.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @param state digital teletext app current state.
+         */
+        public void onTeletextAppStateChanged(
+                @NonNull String iAppServiceId, @TvIAppManager.TeletextAppState int state) {
+        }
+
+        /**
          * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
          */
         public void onSetVideoBounds(@NonNull String iAppServiceId, @NonNull Rect rect) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is called.
+         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+         * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
          */
         public void onRequestCurrentChannelUri(@NonNull String iAppServiceId) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is called.
+         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+         * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
          */
         public void onRequestCurrentChannelLcn(@NonNull String iAppServiceId) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is called.
+         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+         * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
          */
         public void onRequestStreamVolume(@NonNull String iAppServiceId) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is called.
+         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+         * called.
+         *
+         * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
+         */
+        public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
+        }
+
+        /**
+         * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          */
-        public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
+        public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
         }
 
     }
 
     /**
      * Interface definition for a callback to be invoked when the unhandled input event is received.
+     * @hide
      */
     public interface OnUnhandledInputEventListener {
         /**
@@ -685,8 +799,10 @@
         }
 
         @Override
-        public void onCommandRequest(Session session,
-                @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+        public void onCommandRequest(
+                Session session,
+                @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                Bundle parameters) {
             if (DEBUG) {
                 Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
                         + parameters.toString() + ")");
@@ -695,8 +811,16 @@
                 Log.w(TAG, "onCommandRequest - session not created");
                 return;
             }
-            if (mCallback != null) {
-                mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+                            }
+                        }
+                    });
+                }
             }
         }
 
@@ -709,8 +833,16 @@
                 Log.w(TAG, "onSessionStateChanged - session not created");
                 return;
             }
-            if (mCallback != null) {
-                mCallback.onSessionStateChanged(mIAppServiceId, state);
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onSessionStateChanged(mIAppServiceId, state);
+                            }
+                        }
+                    });
+                }
             }
         }
 
@@ -724,8 +856,31 @@
                 Log.w(TAG, "onBiInteractiveAppCreated - session not created");
                 return;
             }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onBiInteractiveAppCreated(
+                                        mIAppServiceId, biIAppUri, biIAppId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onTeletextAppStateChanged(Session session, int state) {
+            if (DEBUG) {
+                Log.d(TAG, "onTeletextAppStateChanged (state=" + state +  ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onTeletextAppStateChanged - session not created");
+                return;
+            }
             if (mCallback != null) {
-                mCallback.onBiInteractiveAppCreated(mIAppServiceId, biIAppUri, biIAppId);
+                mCallback.onTeletextAppStateChanged(mIAppServiceId, state);
             }
         }
 
@@ -738,8 +893,16 @@
                 Log.w(TAG, "onSetVideoBounds - session not created");
                 return;
             }
-            if (mCallback != null) {
-                mCallback.onSetVideoBounds(mIAppServiceId, rect);
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onSetVideoBounds(mIAppServiceId, rect);
+                            }
+                        }
+                    });
+                }
             }
         }
 
@@ -752,8 +915,16 @@
                 Log.w(TAG, "onRequestCurrentChannelUri - session not created");
                 return;
             }
-            if (mCallback != null) {
-                mCallback.onRequestCurrentChannelUri(mIAppServiceId);
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestCurrentChannelUri(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
             }
         }
 
@@ -766,8 +937,16 @@
                 Log.w(TAG, "onRequestCurrentChannelLcn - session not created");
                 return;
             }
-            if (mCallback != null) {
-                mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
             }
         }
 
@@ -780,8 +959,16 @@
                 Log.w(TAG, "onRequestStreamVolume - session not created");
                 return;
             }
-            if (mCallback != null) {
-                mCallback.onRequestStreamVolume(mIAppServiceId);
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestStreamVolume(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
             }
         }
 
@@ -794,8 +981,30 @@
                 Log.w(TAG, "onRequestTrackInfoList - session not created");
                 return;
             }
+            synchronized (mCallbackLock) {
+                if (mCallbackExecutor != null) {
+                    mCallbackExecutor.execute(() -> {
+                        synchronized (mCallbackLock) {
+                            if (mCallback != null) {
+                                mCallback.onRequestTrackInfoList(mIAppServiceId);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCurrentTvInputId(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRequestCurrentTvInputId");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRequestCurrentTvInputId - session not created");
+                return;
+            }
             if (mCallback != null) {
-                mCallback.onRequestTrackInfoList(mIAppServiceId);
+                mCallback.onRequestCurrentTvInputId(mIAppServiceId);
             }
         }
     }
diff --git a/media/java/android/media/tv/tunerresourcemanager/OWNER b/media/java/android/media/tv/tunerresourcemanager/OWNER
index 76b84d9..0eb1c31 100644
--- a/media/java/android/media/tv/tunerresourcemanager/OWNER
+++ b/media/java/android/media/tv/tunerresourcemanager/OWNER
@@ -1,4 +1,3 @@
-amyjojo@google.com
-nchalko@google.com
 quxiangfang@google.com
-shubang@google.com
\ No newline at end of file
+shubang@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
new file mode 100644
index 0000000..fd66d3b
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothProfile;
+import android.media.BtProfileConnectionInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BtProfileConnectionInfoTest {
+
+    @Test
+    public void testCoverageA2dp() {
+        final boolean supprNoisy = false;
+        final int volume = 42;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume);
+        assertEquals(info.getProfile(), BluetoothProfile.A2DP);
+        assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+        assertEquals(info.getVolume(), volume);
+    }
+
+    @Test
+    public void testCoverageA2dpSink() {
+        final int volume = 42;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume);
+        assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK);
+        assertEquals(info.getVolume(), volume);
+    }
+
+    @Test
+    public void testCoveragehearingAid() {
+        final boolean supprNoisy = true;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy);
+        assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID);
+        assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+    }
+
+    @Test
+    public void testCoverageLeAudio() {
+        final boolean supprNoisy = false;
+        final boolean isLeOutput = true;
+        final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy,
+                isLeOutput);
+        assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO);
+        assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+        assertEquals(info.getIsLeOutput(), isLeOutput);
+    }
+}
+
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
index 73ea663..7554889 100644
--- a/media/tests/TunerTest/OWNERS
+++ b/media/tests/TunerTest/OWNERS
@@ -1,4 +1,4 @@
-amyjojo@google.com
-nchalko@google.com
 quxiangfang@google.com
 shubang@google.com
+hgchen@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index deee5b1..fbd4b2e 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -71,11 +71,12 @@
         const AChoreographerFrameCallbackData* data, size_t index) {
     return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(data, index);
+    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(data,
+                                                                                         index);
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(data, index);
+    return AChoreographerFrameCallbackData_routeGetFrameTimelineDeadlineNanos(data, index);
 }
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3c1aa44..35c794e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -34,8 +34,8 @@
     AChoreographerFrameCallbackData_getFrameTimelinesLength;  # introduced=33
     AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineVsyncId;  # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime;  # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineDeadline;  # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos;  # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos;  # introduced=33
     AConfiguration_copy;
     AConfiguration_delete;
     AConfiguration_diff;
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index cc7b2a5..f74edb1 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -142,7 +142,15 @@
         setAugmentWithSubscriptionPlan(true);
     }
 
-    /** @hide */
+    /**
+     * Set poll on open flag to indicate the poll is needed before service gets statistics
+     * result. This is default enabled. However, for any non-privileged caller, the poll might
+     * be omitted in case of rate limiting.
+     *
+     * @param pollOnOpen true if poll is needed.
+     * @hide
+     */
+    // @SystemApi(client = MODULE_LIBRARIES)
     public void setPollOnOpen(boolean pollOnOpen) {
         if (pollOnOpen) {
             mFlags |= FLAG_POLL_ON_OPEN;
@@ -863,4 +871,74 @@
             return msg.getData().getParcelable(key);
         }
     }
+
+    /**
+     * Mark given UID as being in foreground for stats purposes.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setUidForeground(int uid, boolean uidForeground) {
+        try {
+            mService.setUidForeground(uid, uidForeground);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Advise persistence threshold; may be overridden internally.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void advisePersistThreshold(long thresholdBytes) {
+        try {
+            mService.advisePersistThreshold(thresholdBytes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Force update of statistics.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void forceUpdate() {
+        try {
+            mService.forceUpdate();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+            long limit) {
+        try {
+            mService.setStatsProviderWarningAndLimitAsync(iface, warning, limit);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index 12937b5..a4babb5 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -94,4 +94,16 @@
     /** Registers a network stats provider */
     INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
             in INetworkStatsProvider provider);
+
+    /** Mark given UID as being in foreground for stats purposes. */
+    void setUidForeground(int uid, boolean uidForeground);
+
+    /** Advise persistence threshold; may be overridden internally. */
+    void advisePersistThreshold(long thresholdBytes);
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+     void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 0d15dff..49aa99b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -25,7 +26,6 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.net.annotations.PolicyDirection;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -41,6 +41,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -88,6 +90,11 @@
     @SystemApi(client = MODULE_LIBRARIES)
     public static final int DIRECTION_FWD = 2;
 
+    /** @hide */
+    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PolicyDirection {}
+
     /**
      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
      *
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index b00fea4..9d532e7 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -383,6 +383,95 @@
             this.operations += another.operations;
         }
 
+        /**
+         * @return interface name of this entry.
+         * @hide
+         */
+        @Nullable public String getIface() {
+            return iface;
+        }
+
+        /**
+         * @return the uid of this entry.
+         */
+        public int getUid() {
+            return uid;
+        }
+
+        /**
+         * @return the set state of this entry. Should be one of the following
+         * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+         */
+        @State public int getSet() {
+            return set;
+        }
+
+        /**
+         * @return the tag value of this entry.
+         */
+        public int getTag() {
+            return tag;
+        }
+
+        /**
+         * @return the metered state. Should be one of the following
+         * values: {link #METERED_YES}, {link #METERED_NO}.
+         */
+        @Meteredness public int getMetered() {
+            return metered;
+        }
+
+        /**
+         * @return the roaming state. Should be one of the following
+         * values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+         */
+        @Roaming public int getRoaming() {
+            return roaming;
+        }
+
+        /**
+         * @return the default network state. Should be one of the following
+         * values: {link #DEFAULT_NETWORK_YES}, {link #DEFAULT_NETWORK_NO}.
+         */
+        @DefaultNetwork public int getDefaultNetwork() {
+            return defaultNetwork;
+        }
+
+        /**
+         * @return the number of received bytes.
+         */
+        public long getRxBytes() {
+            return rxBytes;
+        }
+
+        /**
+         * @return the number of received packets.
+         */
+        public long getRxPackets() {
+            return rxPackets;
+        }
+
+        /**
+         * @return the number of transmitted bytes.
+         */
+        public long getTxBytes() {
+            return txBytes;
+        }
+
+        /**
+         * @return the number of transmitted packets.
+         */
+        public long getTxPackets() {
+            return txPackets;
+        }
+
+        /**
+         * @return the count of network operations performed for this entry.
+         */
+        public long getOperations() {
+            return operations;
+        }
+
         @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder();
@@ -593,7 +682,7 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public Entry getValues(int i, Entry recycle) {
+    public Entry getValues(int i, @Nullable Entry recycle) {
         final Entry entry = recycle != null ? recycle : new Entry();
         entry.iface = iface[i];
         entry.uid = uid[i];
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 7935d28..9f9d73f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -46,8 +46,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
 import com.android.internal.util.FileRotator;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkStatsUtils;
@@ -58,6 +56,7 @@
 import java.io.DataInput;
 import java.io.DataInputStream;
 import java.io.DataOutput;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -83,9 +82,6 @@
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
-    /** Default buffer size from BufferedInputStream */
-    private static final int BUFFER_SIZE = 8192;
-
     private static final int VERSION_NETWORK_INIT = 1;
 
     private static final int VERSION_UID_INIT = 1;
@@ -439,8 +435,7 @@
 
     @Override
     public void read(InputStream in) throws IOException {
-        final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
-        read(dataIn);
+        read((DataInput) new DataInputStream(in));
     }
 
     private void read(DataInput in) throws IOException {
@@ -479,9 +474,8 @@
 
     @Override
     public void write(OutputStream out) throws IOException {
-        final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
-        write(dataOut);
-        dataOut.flush();
+        write((DataOutput) new DataOutputStream(out));
+        out.flush();
     }
 
     private void write(DataOutput out) throws IOException {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index d8feb88..1af32bf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -27,8 +28,8 @@
 import android.content.Context;
 import android.media.MediaPlayer;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 
 import com.android.server.NetworkManagementSocketTagger;
 
@@ -36,6 +37,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.net.SocketException;
@@ -53,6 +56,7 @@
  * use {@link NetworkStatsManager} instead.
  */
 public class TrafficStats {
+    private static final String TAG = TrafficStats.class.getSimpleName();
     /**
      * The return value to indicate that the device does not support the statistic.
      */
@@ -173,12 +177,25 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
     private synchronized static INetworkStatsService getStatsService() {
         if (sStatsService == null) {
-            sStatsService = INetworkStatsService.Stub.asInterface(
-                    ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+            sStatsService = getStatsBinder();
         }
         return sStatsService;
     }
 
+    @Nullable
+    private static INetworkStatsService getStatsBinder() {
+        try {
+            final Method getServiceMethod = Class.forName("android.os.ServiceManager")
+                    .getDeclaredMethod("getService", new Class[]{String.class});
+            final IBinder binder = (IBinder) getServiceMethod.invoke(
+                    null, Context.NETWORK_STATS_SERVICE);
+            return INetworkStatsService.Stub.asInterface(binder);
+        } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException
+                | InvocationTargetException e) {
+            throw new NullPointerException("Cannot get INetworkStatsService: " + e);
+        }
+    }
+
     /**
      * Snapshot of {@link NetworkStats} when the currently active profiling
      * session started, or {@code null} if no session active.
@@ -265,6 +282,18 @@
     }
 
     /**
+     * Set active tag to use when accounting {@link Socket} traffic originating
+     * from the current thread. The tag used internally is well-defined to
+     * distinguish all download provider traffic.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void setThreadStatsTagDownload() {
+        setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
+    }
+
+    /**
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
index e35f6a6..1eb52fb 100644
--- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
+++ b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
@@ -26,6 +26,7 @@
 
 /**
  * Assigns tags to sockets for traffic stats.
+ * @hide
  */
 public final class NetworkManagementSocketTagger extends SocketTagger {
     private static final String TAG = "NetworkManagementSocketTagger";
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
deleted file mode 100644
index 0e9a9da..0000000
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import android.annotation.NonNull;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-
-public abstract class NetworkStatsManagerInternal {
-    /** Return network layer usage total for traffic that matches template. */
-    public abstract long getNetworkTotalBytes(NetworkTemplate template, long start, long end);
-
-    /** Return network layer usage per-UID for traffic that matches template. */
-    public abstract NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end);
-
-    /** Mark given UID as being in foreground for stats purposes. */
-    public abstract void setUidForeground(int uid, boolean uidForeground);
-
-    /** Advise persistance threshold; may be overridden internally. */
-    public abstract void advisePersistThreshold(long thresholdBytes);
-
-    /** Force update of statistics. */
-    public abstract void forceUpdate();
-
-    /**
-     * Set the warning and limit to all registered custom network stats providers.
-     * Note that invocation of any interface will be sent to all providers.
-     */
-    public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
-            long limit);
-}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 3273e88..e15acf3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -96,6 +96,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkIdentitySet;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
@@ -155,7 +156,6 @@
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
-import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 
 import java.io.File;
@@ -209,6 +209,14 @@
 
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+      */
+    private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
+    private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+
     private final Context mContext;
     private final NetworkStatsFactory mStatsFactory;
     private final AlarmManager mAlarmManager;
@@ -423,7 +431,6 @@
                 new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
-        service.registerLocalService();
 
         return service;
     }
@@ -504,11 +511,6 @@
         }
     }
 
-    private void registerLocalService() {
-        LocalServices.addService(NetworkStatsManagerInternal.class,
-                new NetworkStatsManagerInternalImpl());
-    }
-
     /**
      * Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
      */
@@ -860,7 +862,7 @@
             if (LOGD) Log.d(TAG, "Resolving plan for " + template);
             final long token = Binder.clearCallingIdentity();
             try {
-                plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+                plan = mContext.getSystemService(NetworkPolicyManager.class)
                         .getSubscriptionPlan(template);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -999,7 +1001,8 @@
     }
 
     @VisibleForTesting
-    void setUidForeground(int uid, boolean uidForeground) {
+    public void setUidForeground(int uid, boolean uidForeground) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
             final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
@@ -1035,7 +1038,7 @@
 
     @Override
     public void forceUpdate() {
-        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1045,7 +1048,9 @@
         }
     }
 
-    private void advisePersistThreshold(long thresholdBytes) {
+    /** Advise persistence threshold; may be overridden internally. */
+    public void advisePersistThreshold(long thresholdBytes) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         // clamp threshold into safe range
         mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
                 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
@@ -1624,7 +1629,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsMobileSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_MOBILE_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1636,7 +1641,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsWifiSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_WIFI_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1682,52 +1687,19 @@
         removeUidsLocked(CollectionUtils.toIntArray(uids));
     }
 
-    private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
-        @Override
-        public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
-            try {
-                return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+    public void setStatsProviderWarningAndLimitAsync(
+            @NonNull String iface, long warning, long limit) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        if (LOGV) {
+            Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+                    + iface + "," + warning + "," + limit + ")");
         }
-
-        @Override
-        public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
-            try {
-                return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
-        }
-
-        @Override
-        public void setUidForeground(int uid, boolean uidForeground) {
-            NetworkStatsService.this.setUidForeground(uid, uidForeground);
-        }
-
-        @Override
-        public void advisePersistThreshold(long thresholdBytes) {
-            NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
-        }
-
-        @Override
-        public void forceUpdate() {
-            NetworkStatsService.this.forceUpdate();
-        }
-
-        @Override
-        public void setStatsProviderWarningAndLimitAsync(
-                @NonNull String iface, long warning, long limit) {
-            if (LOGV) {
-                Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
-                        + iface + "," + warning + "," + limit + ")");
-            }
-            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
-                    warning, limit));
-        }
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
+                warning, limit));
     }
 
     @Override
@@ -2031,10 +2003,12 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         Objects.requireNonNull(provider, "provider is null");
         Objects.requireNonNull(tag, "tag is null");
+        final NetworkPolicyManager netPolicyManager = mContext
+                .getSystemService(NetworkPolicyManager.class);
         try {
             NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
                     tag, provider, mStatsProviderSem, mAlertObserver,
-                    mStatsProviderCbList);
+                    mStatsProviderCbList, netPolicyManager);
             mStatsProviderCbList.add(callback);
             Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
                     + getCallingUid() + "/" + getCallingPid());
@@ -2076,6 +2050,7 @@
         @NonNull private final Semaphore mSemaphore;
         @NonNull final AlertObserver mAlertObserver;
         @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+        @NonNull final NetworkPolicyManager mNetworkPolicyManager;
 
         @NonNull private final Object mProviderStatsLock = new Object();
 
@@ -2089,7 +2064,8 @@
                 @NonNull String tag, @NonNull INetworkStatsProvider provider,
                 @NonNull Semaphore semaphore,
                 @NonNull AlertObserver alertObserver,
-                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
+                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList,
+                @NonNull NetworkPolicyManager networkPolicyManager)
                 throws RemoteException {
             mTag = tag;
             mProvider = provider;
@@ -2097,6 +2073,7 @@
             mSemaphore = semaphore;
             mAlertObserver = alertObserver;
             mStatsProviderCbList = cbList;
+            mNetworkPolicyManager = networkPolicyManager;
         }
 
         @NonNull
@@ -2143,8 +2120,7 @@
         public void notifyWarningOrLimitReached() {
             Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
             BinderUtils.withCleanCallingIdentity(() ->
-                    LocalServices.getService(NetworkPolicyManagerInternal.class)
-                            .onStatsProviderWarningOrLimitReached(mTag));
+                    mNetworkPolicyManager.onStatsProviderWarningOrLimitReached());
         }
 
         @Override
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 7a239af..068074a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -44,6 +44,7 @@
 
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /** Basic fused location provider implementation. */
 public class FusedLocationProvider extends LocationProviderBase {
@@ -69,6 +70,12 @@
     private final BroadcastReceiver mUserChangeReceiver;
 
     @GuardedBy("mLock")
+    boolean mGpsPresent;
+
+    @GuardedBy("mLock")
+    boolean mNlpPresent;
+
+    @GuardedBy("mLock")
     private ProviderRequest mRequest;
 
     @GuardedBy("mLock")
@@ -119,19 +126,28 @@
 
     @Override
     public void onFlush(OnFlushCompleteCallback callback) {
-        OnFlushCompleteCallback wrapper = new OnFlushCompleteCallback() {
-            private int mFlushCount = 2;
+        synchronized (mLock) {
+            AtomicInteger flushCount = new AtomicInteger(0);
+            if (mGpsPresent) {
+                flushCount.incrementAndGet();
+            }
+            if (mNlpPresent) {
+                flushCount.incrementAndGet();
+            }
 
-            @Override
-            public void onFlushComplete() {
-                if (--mFlushCount == 0) {
+            OnFlushCompleteCallback wrapper = () -> {
+                if (flushCount.decrementAndGet() == 0) {
                     callback.onFlushComplete();
                 }
-            }
-        };
+            };
 
-        mGpsListener.flush(wrapper);
-        mNetworkListener.flush(wrapper);
+            if (mGpsPresent) {
+                mGpsListener.flush(wrapper);
+            }
+            if (mNlpPresent) {
+                mNetworkListener.flush(wrapper);
+            }
+        }
     }
 
     @Override
@@ -139,9 +155,19 @@
 
     @GuardedBy("mLock")
     private void updateRequirementsLocked() {
-        long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getIntervalMillis()
-                : INTERVAL_DISABLED;
-        long networkInterval = mRequest.getIntervalMillis();
+        // it's possible there might be race conditions on device start where a provider doesn't
+        // appear to be present yet, but once a provider is present it shouldn't go away.
+        if (!mGpsPresent) {
+            mGpsPresent = mLocationManager.hasProvider(GPS_PROVIDER);
+        }
+        if (!mNlpPresent) {
+            mNlpPresent = mLocationManager.hasProvider(NETWORK_PROVIDER);
+        }
+
+        long gpsInterval =
+                mGpsPresent && (!mNlpPresent || mRequest.getQuality() < QUALITY_LOW_POWER)
+                        ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
+        long networkInterval = mNlpPresent ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
 
         mGpsListener.resetProviderRequest(gpsInterval);
         mNetworkListener.resetProviderRequest(networkInterval);
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index b266df5..fcf2282 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -49,6 +49,7 @@
         "SettingsLibTwoTargetPreference",
         "SettingsLibSettingsTransition",
         "SettingsLibActivityEmbedding",
+        "SettingsLibButtonPreference",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
new file mode 100644
index 0000000..39f804f
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibButtonPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.preference_preference",
+        "SettingsLibSettingsTheme",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ButtonPreference/AndroidManifest.xml b/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
new file mode 100644
index 0000000..2d35c331
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 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.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_background_material.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_background_material.xml
new file mode 100644
index 0000000..51ca4ac
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_background_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+
+<!-- Used for the text of a bordered colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/colorButtonNormal" />
+    <item android:color="?android:attr/colorAccent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_text_material.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_text_material.xml
new file mode 100644
index 0000000..8dca4db
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_btn_colored_text_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+
+<!-- Used for the text of a bordered colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/textColorPrimary" />
+    <item android:color="?android:attr/textColorPrimaryInverse" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_dark.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_dark.xml
new file mode 100644
index 0000000..1e930ea
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="@dimen/settingslib_highlight_alpha_material_dark"
+          android:color="@android:color/white" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_light.xml b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_light.xml
new file mode 100644
index 0000000..378fc16
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/color/settingslib_ripple_material_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="@dimen/settingslib_highlight_alpha_material_light"
+          android:color="@android:color/black" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/drawable/settingslib_btn_colored_material.xml b/packages/SettingsLib/ButtonPreference/res/drawable/settingslib_btn_colored_material.xml
new file mode 100644
index 0000000..bb0597d
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/drawable/settingslib_btn_colored_material.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="4dp"
+       android:insetTop="6dp"
+       android:insetRight="4dp"
+       android:insetBottom="6dp">
+    <ripple android:color="?attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle"
+                   android:tint="@color/settingslib_btn_colored_background_material">
+                <corners android:radius="2dp" />
+                <solid android:color="@android:color/white" />
+                <padding android:left="8dp"
+                         android:top="4dp"
+                         android:right="8dp"
+                         android:bottom="4dp" />
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
new file mode 100644
index 0000000..1ff0990
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <Button
+        android:id="@+id/settingslib_button"
+        android:drawablePadding="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:layout_marginStart="-4dp"
+        style="@style/SettingsLibButtonStyle" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/ButtonPreference/res/values-night/colors.xml b/packages/SettingsLib/ButtonPreference/res/values-night/colors.xml
new file mode 100644
index 0000000..6be7b93
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-night/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Material inverse ripple color, useful for inverted backgrounds. -->
+    <color name="settingslib_button_ripple">@color/settingslib_ripple_material_light</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v23/styles.xml b/packages/SettingsLib/ButtonPreference/res/values-v23/styles.xml
new file mode 100644
index 0000000..202645f
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-v23/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibButtonStyle" parent="android:Widget.Material.Button.Colored">
+        <item name="android:theme">@style/SettingsLibRoundedCornerThemeOverlay</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/settingslib_btn_colored_text_material</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v28/styles.xml b/packages/SettingsLib/ButtonPreference/res/values-v28/styles.xml
new file mode 100644
index 0000000..d8c6ac3f
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-v28/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibButtonStyle" parent="android:Widget.DeviceDefault.Button.Colored">
+        <item name="android:theme">@style/SettingsLibRoundedCornerThemeOverlay</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/res/values-v31/styles.xml b/packages/SettingsLib/ButtonPreference/res/values-v31/styles.xml
new file mode 100644
index 0000000..12dcbbf
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values-v31/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibRoundedCornerThemeOverlay">
+        <item name="android:buttonCornerRadius">24dp</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">16dp</item>
+        <item name="android:colorControlHighlight">@color/settingslib_button_ripple</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/res/values/attrs.xml b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
new file mode 100644
index 0000000..9a4312a
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <declare-styleable name="ButtonPreference">
+        <attr name="android:gravity" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/colors.xml b/packages/SettingsLib/ButtonPreference/res/values/colors.xml
new file mode 100644
index 0000000..45baeeb
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Material inverse ripple color, useful for inverted backgrounds. -->
+    <color name="settingslib_button_ripple">@color/settingslib_ripple_material_dark</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/dimens.xml b/packages/SettingsLib/ButtonPreference/res/values/dimens.xml
new file mode 100644
index 0000000..3d7831e
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <item name="settingslib_highlight_alpha_material_light" format="float" type="dimen">0.10</item>
+    <item name="settingslib_highlight_alpha_material_dark" format="float" type="dimen">0.10</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ButtonPreference/res/values/styles.xml b/packages/SettingsLib/ButtonPreference/res/values/styles.xml
new file mode 100644
index 0000000..3963732
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/res/values/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="SettingsLibRoundedCornerThemeOverlay">
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">16dp</item>
+        <item name="android:colorControlHighlight">@color/settingslib_button_ripple</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle" parent="android:Widget.Material.Button">
+        <item name="android:theme">@style/SettingsLibRoundedCornerThemeOverlay</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/settingslib_btn_colored_text_material</item>
+        <item name="android:background">@drawable/settingslib_btn_colored_material</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
new file mode 100644
index 0000000..56d2967
--- /dev/null
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 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.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.annotation.GravityInt;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference handled a button
+ */
+public class ButtonPreference extends Preference {
+
+    private static final int ICON_SIZE = 24;
+
+    private View.OnClickListener mClickListener;
+    private Button mButton;
+    private CharSequence mTitle;
+    private Drawable mIcon;
+    @GravityInt
+    private int mGravity;
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme, the supplied
+     * attribute set, and default style attribute.
+     *
+     * @param context      The Context the view is running in, through which it can
+     *                     access the current theme, resources, etc.
+     * @param attrs        The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     */
+    public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and the supplied
+     * attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs   The attributes of the XML tag that is inflating the view.
+     */
+    public ButtonPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, 0 /* defStyleAttr */);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and a customized view.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     */
+    public ButtonPreference(Context context) {
+        this(context, null);
+    }
+
+    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+        setLayoutResource(R.layout.settingslib_button_layout);
+
+        if (attrs != null) {
+            TypedArray a = context.obtainStyledAttributes(attrs,
+                    androidx.preference.R.styleable.Preference, defStyleAttr,
+                    0 /*defStyleRes*/);
+            mTitle = a.getText(
+                    androidx.preference.R.styleable.Preference_android_title);
+            mIcon = a.getDrawable(
+                    androidx.preference.R.styleable.Preference_android_icon);
+            a.recycle();
+
+            a = context.obtainStyledAttributes(attrs,
+                    R.styleable.ButtonPreference, defStyleAttr,
+                    0 /*defStyleRes*/);
+            mGravity = a.getInt(R.styleable.ButtonPreference_android_gravity, Gravity.START);
+            a.recycle();
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        mButton = (Button) holder.findViewById(R.id.settingslib_button);
+        setTitle(mTitle);
+        setIcon(mIcon);
+        setGravity(mGravity);
+        setOnClickListener(mClickListener);
+
+        if (mButton != null) {
+            final boolean selectable = isSelectable();
+            mButton.setFocusable(selectable);
+            mButton.setClickable(selectable);
+
+            mButton.setEnabled(isEnabled());
+        }
+
+        holder.setDividerAllowedAbove(false);
+        holder.setDividerAllowedBelow(false);
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitle = title;
+        if (mButton != null) {
+            mButton.setText(title);
+        }
+    }
+
+    @Override
+    public void setIcon(Drawable icon) {
+        mIcon = icon;
+        if (mButton == null || icon == null) {
+            return;
+        }
+        //get pixel from dp
+        int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, ICON_SIZE,
+                getContext().getResources().getDisplayMetrics());
+        icon.setBounds(0, 0, size, size);
+
+        //set drawableStart
+        mButton.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null/* top */, null/* end */,
+                null/* bottom */);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        if (mButton != null) {
+            mButton.setEnabled(enabled);
+        }
+    }
+
+    /**
+     * Return Button
+     */
+    public Button getButton() {
+        return mButton;
+    }
+
+
+    /**
+     * Set a listener for button click
+     */
+    public void setOnClickListener(View.OnClickListener listener) {
+        mClickListener = listener;
+        if (mButton != null) {
+            mButton.setOnClickListener(listener);
+        }
+    }
+
+    /**
+     * Set the gravity of button
+     *
+     * @param gravity The {@link Gravity} supported CENTER_HORIZONTAL
+     *                and the default value is START
+     */
+    public void setGravity(@GravityInt int gravity) {
+        if (gravity == Gravity.CENTER_HORIZONTAL || gravity == Gravity.CENTER_VERTICAL
+                || gravity == Gravity.CENTER) {
+            mGravity = Gravity.CENTER_HORIZONTAL;
+        } else {
+            mGravity = Gravity.START;
+        }
+
+        if (mButton != null) {
+            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mButton.getLayoutParams();
+            lp.gravity = mGravity;
+            mButton.setLayoutParams(lp);
+        }
+    }
+}
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 4cdcca7..5222d8a 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -250,7 +250,7 @@
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Konektatuta daudenak"</string>
     <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Gailuaren xehetasunak"</string>
     <string name="adb_device_forget" msgid="193072400783068417">"Ahaztu"</string>
-    <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Gailuaren erreferentzia-gako digitala: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
+    <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Gailuaren aztarna digitala: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
     <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Ezin izan da konektatu"</string>
     <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Ziurtatu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> sare berera konektatuta dagoela"</string>
     <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Parekatu gailuarekin"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 9490ede..4103a9f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -209,7 +209,7 @@
     <string name="tts_status_checking" msgid="8026559918948285013">"Tarkistetaan…"</string>
     <string name="tts_engine_settings_title" msgid="7849477533103566291">"Asetukset: <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
     <string name="tts_engine_settings_button" msgid="477155276199968948">"Käynnistä moottorin asetukset"</string>
-    <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ensisijainen kone"</string>
+    <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Ensisijainen moottori"</string>
     <string name="tts_general_section_title" msgid="8919671529502364567">"Yleiset"</string>
     <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Palauta äänenkorkeus"</string>
     <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Palauta tekstin lukemisen oletusäänenkorkeus"</string>
@@ -220,8 +220,8 @@
     <item msgid="1158955023692670059">"Nopea"</item>
     <item msgid="5664310435707146591">"Nopeampi"</item>
     <item msgid="5491266922147715962">"Hyvin nopea"</item>
-    <item msgid="7659240015901486196">"Nopea"</item>
-    <item msgid="7147051179282410945">"Erittäin nopea"</item>
+    <item msgid="7659240015901486196">"Vauhdikas"</item>
+    <item msgid="7147051179282410945">"Erittäin vauhdikas"</item>
     <item msgid="581904787661470707">"Nopein"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Valitse profiili"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 47b0744..f9ac01d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1271,10 +1271,13 @@
     <string name="wifi_status_mac_randomized">MAC is randomized</string>
 
     <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
-    <plurals name="wifi_tether_connected_summary">
-        <item quantity="one">%1$d device connected</item>
-        <item quantity="other">%1$d devices connected</item>
-    </plurals>
+    <string name="wifi_tether_connected_summary">
+        {count, plural,
+            =0    {0 device connected}
+            =1    {1 device connected}
+            other {# devices connected}
+        }
+    </string>
 
     <!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_manual_zen_more_time">More time.</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 56454e9..4ab6542 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
+import android.icu.text.MessageFormat;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
@@ -33,6 +34,8 @@
 
 import com.android.settingslib.R;
 
+import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 public class WifiUtils {
@@ -333,4 +336,20 @@
         intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
         return intent;
     }
+
+    /**
+     * Returns the string of Wi-Fi tethering summary for connected devices.
+     *
+     * @param context          The application context
+     * @param connectedDevices The count of connected devices
+     */
+    public static String getWifiTetherSummaryForConnectedDevices(Context context,
+            int connectedDevices) {
+        MessageFormat msgFormat = new MessageFormat(
+                context.getResources().getString(R.string.wifi_tether_connected_summary),
+                Locale.getDefault());
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("count", connectedDevices);
+        return msgFormat.format(arguments);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
new file mode 100644
index 0000000..625b214
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.R;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowDrawable;
+
+@RunWith(RobolectricTestRunner.class)
+public class ButtonPreferenceTest {
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private ButtonPreference mPreference;
+    private PreferenceViewHolder mHolder;
+
+    private boolean mClickListenerCalled;
+    private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true;
+
+    @Before
+    public void setUp() {
+        mClickListenerCalled = false;
+        mPreference = new ButtonPreference(mContext);
+        setUpViewHolder();
+    }
+
+    @Test
+    public void onBindViewHolder_whenTitleSet_shouldSetButtonText() {
+        final String testTitle = "Test title";
+        mPreference.setTitle(testTitle);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        assertThat(button.getText().toString()).isEqualTo(testTitle);
+    }
+
+    @Test
+    public void onBindViewHolder_whenIconSet_shouldSetIcon() {
+        mPreference.setIcon(R.drawable.settingslib_ic_cross);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final Drawable icon = button.getCompoundDrawablesRelative()[0];
+        final ShadowDrawable shadowDrawable = shadowOf(icon);
+        assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.settingslib_ic_cross);
+    }
+
+    @Test
+    public void onBindViewHolder_setEnable_shouldSetButtonEnabled() {
+        mPreference.setEnabled(true);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        assertThat(button.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_setDisable_shouldSetButtonDisabled() {
+        mPreference.setEnabled(false);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        assertThat(button.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_default_shouldReturnButtonGravityStart() {
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.START);
+    }
+
+    @Test
+    public void onBindViewHolder_setGravityStart_shouldReturnButtonGravityStart() {
+        mPreference.setGravity(Gravity.START);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.START);
+    }
+
+    @Test
+    public void onBindViewHolder_setGravityCenter_shouldReturnButtonGravityCenterHorizontal() {
+        mPreference.setGravity(Gravity.CENTER_HORIZONTAL);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+
+        mPreference.setGravity(Gravity.CENTER_VERTICAL);
+        mPreference.onBindViewHolder(mHolder);
+        assertThat(lp.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+
+        mPreference.setGravity(Gravity.CENTER);
+        mPreference.onBindViewHolder(mHolder);
+        assertThat(lp.gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+    }
+
+    @Test
+    public void onBindViewHolder_setUnsupportedGravity_shouldReturnButtonGravityStart() {
+        mPreference.setGravity(Gravity.END);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        final Button button = mPreference.getButton();
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) button.getLayoutParams();
+        assertThat(lp.gravity).isEqualTo(Gravity.START);
+    }
+
+    @Test
+    public void setButtonOnClickListener_setsClickListener() {
+        mPreference.setOnClickListener(mClickListener);
+
+        mPreference.onBindViewHolder(mHolder);
+        final Button button = mPreference.getButton();
+        button.callOnClick();
+
+        assertThat(mClickListenerCalled).isTrue();
+    }
+
+    private void setUpViewHolder() {
+        final View rootView =
+                View.inflate(mContext, mPreference.getLayoutResource(), null /* parent */);
+        mHolder = PreferenceViewHolder.createInstanceForTests(rootView);
+    }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1303a62..8546b16 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -341,6 +341,9 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />
 
+    <!-- Permission needed to test wallpaper dimming -->
+    <uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
+
     <!-- Permission required to test ContentResolver caching. -->
     <uses-permission android:name="android.permission.CACHE_CONTENT" />
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a741514..e9e85f1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -855,6 +855,12 @@
             android:singleUser="true"
             android:permission="android.permission.BIND_DREAM_SERVICE" />
 
+        <!-- Service for external clients to do media transfer -->
+        <!-- TODO(b/203800643): Export and guard with a permission. -->
+        <service
+            android:name=".media.taptotransfer.sender.MediaTttSenderService"
+           />
+
         <receiver
             android:name=".tuner.TunerService$ClearReceiver"
             android:exported="false">
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9c35fea..65f22b8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -738,4 +738,6 @@
     <!-- Class for the communal source connector to be used -->
     <string name="config_communalSourceConnector" translatable="false"></string>
 
+    <!-- How often in milliseconds to jitter the dream overlay in order to avoid burn-in. -->
+    <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">500</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2b6c9f5..08fb2c6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2152,8 +2152,8 @@
     <!--- ****** Media tap-to-transfer ****** -->
     <!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
     <string name="media_transfer_undo">Undo</string>
-    <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play music on the different device. [CHAR LIMIT=75] -->
-    <string name="media_move_closer_to_transfer">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+    <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play media on the different device. [CHAR LIMIT=75] -->
+    <string name="media_move_closer_to_start_cast">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
     <!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
     <string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
 
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index d172006..3cf5bc1 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -49,9 +49,12 @@
         "PluginCoreLib",
         "androidx.dynamicanimation_dynamicanimation",
         "androidx.concurrent_concurrent-futures",
+        "dagger2",
+        "jsr330",
     ],
     java_version: "1.8",
     min_sdk_version: "current",
+    plugins: ["dagger2-compiler"],
 }
 
 java_library {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java
rename to packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Main.java
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
similarity index 81%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
index 6041460..861a4ed 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package com.android.systemui.shared.mediattt;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+parcelable DeviceInfo;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
new file mode 100644
index 0000000..d41aaf3
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.shared.mediattt
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * Represents a device that can send or receive media. Includes any device information necessary for
+ * SysUI to display an informative chip to the user.
+ */
+class DeviceInfo(val name: String) : Parcelable {
+    constructor(parcel: Parcel) : this(parcel.readString())
+
+    override fun writeToParcel(dest: Parcel?, flags: Int) {
+        dest?.writeString(name)
+    }
+
+    override fun describeContents() = 0
+
+    override fun toString() = "name: $name"
+
+    companion object CREATOR : Parcelable.Creator<DeviceInfo> {
+        override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel)
+        override fun newArray(size: Int) = arrayOfNulls<DeviceInfo?>(size)
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
new file mode 100644
index 0000000..484791d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.shared.mediattt;
+
+import android.media.MediaRoute2Info;
+import com.android.systemui.shared.mediattt.DeviceInfo;
+
+/**
+ * A callback interface that can be invoked to trigger media transfer events on System UI.
+ *
+ * This interface is for the *sender* device, which is the device currently playing media. This
+ * sender device can transfer the media to a different device, called the receiver.
+ *
+ * System UI will implement this interface and other services will invoke it.
+ */
+interface IDeviceSenderCallback {
+    /**
+     * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
+     * the user can potentially *start* a cast to the receiver device if the user moves their device
+     * a bit closer.
+     *
+     * Important notes:
+     *   - When this callback triggers, the device is close enough to inform the user that
+     *     transferring is an option, but the device is *not* close enough to actually initiate a
+     *     transfer yet.
+     *   - This callback is for *starting* a cast. It should be used when this device is currently
+     *     playing media locally and the media should be transferred to be played on the receiver
+     *     device instead.
+     */
+    oneway void closeToReceiverToStartCast(
+        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 7539f99..851ea65 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -137,7 +137,8 @@
         filter.addAction(PLUGIN_CHANGED);
         filter.addAction(DISABLE_PLUGIN);
         filter.addDataScheme("package");
-        mContext.registerReceiver(this, filter, PluginActionManager.PLUGIN_PERMISSION, null);
+        mContext.registerReceiver(this, filter, PluginActionManager.PLUGIN_PERMISSION, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiver(this, filter);
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index d182399..54c798c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -60,8 +60,6 @@
     // See IRecentTasks.aidl
     public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
 
-    public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
-            WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
     public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
             WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
     public static final String NAV_BAR_MODE_GESTURAL_OVERLAY =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 8d26ddd..618d2d2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -183,8 +183,8 @@
                     }
                     // Make wallpaper visible immediately since launcher apparently won't do this.
                     for (int i = wallpapersCompat.length - 1; i >= 0; --i) {
-                        t.show(wallpapersCompat[i].leash.getSurfaceControl());
-                        t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);
+                        t.show(wallpapersCompat[i].leash);
+                        t.setAlpha(wallpapersCompat[i].leash, 1.f);
                     }
                 } else {
                     if (launcherTask != null) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 4ec65d8..72e3e18 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -57,7 +57,7 @@
     public final int activityType;
 
     public final int taskId;
-    public final SurfaceControlCompat leash;
+    public final SurfaceControl leash;
     public final boolean isTranslucent;
     public final Rect clipRect;
     public final int prefixOrderIndex;
@@ -82,7 +82,7 @@
     public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
         taskId = app.taskId;
         mode = app.mode;
-        leash = new SurfaceControlCompat(app.leash);
+        leash = app.leash;
         isTranslucent = app.isTranslucent;
         clipRect = app.clipRect;
         position = app.position;
@@ -119,7 +119,7 @@
 
     public RemoteAnimationTarget unwrap() {
         return new RemoteAnimationTarget(
-                taskId, mode, leash.getSurfaceControl(), isTranslucent, clipRect, contentInsets,
+                taskId, mode, leash, isTranslucent, clipRect, contentInsets,
                 prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfiguration,
                 isNotInRecents, mStartLeash, startBounds, taskInfo, allowEnterPip, windowType
         );
@@ -211,7 +211,7 @@
         mode = newModeToLegacyMode(change.getMode());
 
         // TODO: once we can properly sync transactions across process, then get rid of this leash.
-        leash = new SurfaceControlCompat(createLeash(info, change, order, t));
+        leash = createLeash(info, change, order, t);
 
         isTranslucent = (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0
                 || (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0;
@@ -273,7 +273,7 @@
                     info.getChanges().size() - i, info, t));
             if (leashMap == null) continue;
             leashMap.put(info.getChanges().get(i).getLeash(),
-                    out.get(out.size() - 1).leash.mSurfaceControl);
+                    out.get(out.size() - 1).leash);
         }
         return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
     }
@@ -282,8 +282,8 @@
      * @see SurfaceControl#release()
      */
     public void release() {
-        if (leash.mSurfaceControl != null) {
-            leash.mSurfaceControl.release();
+        if (leash != null) {
+            leash.release();
         }
         if (mStartLeash != null) {
             mStartLeash.release();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 2d5080e..2ae32c7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -149,7 +149,7 @@
                 }
                 // Also make all the wallpapers opaque since we want the visible from the start
                 for (int i = wallpapers.length - 1; i >= 0; --i) {
-                    t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
+                    t.setAlpha(wallpapers[i].leash, 1);
                 }
                 t.apply();
                 mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
@@ -267,10 +267,10 @@
                 // We are receiving new opening tasks, so convert to onTasksAppeared.
                 final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
                         openingTasks.get(i), layer, info, t);
-                mLeashMap.put(mOpeningLeashes.get(i), target.leash.mSurfaceControl);
-                t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
-                t.setLayer(target.leash.mSurfaceControl, layer);
-                t.hide(target.leash.mSurfaceControl);
+                mLeashMap.put(mOpeningLeashes.get(i), target.leash);
+                t.reparent(target.leash, mInfo.getRootLeash());
+                t.setLayer(target.leash, layer);
+                t.hide(target.leash);
                 targets[i] = target;
             }
             t.apply();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
deleted file mode 100644
index acc6913..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewRootImpl;
-
-/**
- * TODO: Remove this class
- */
-public class SurfaceControlCompat {
-    final SurfaceControl mSurfaceControl;
-
-    public SurfaceControlCompat(SurfaceControl surfaceControl) {
-        mSurfaceControl = surfaceControl;
-    }
-
-    public SurfaceControlCompat(View v) {
-        ViewRootImpl viewRootImpl = v.getViewRootImpl();
-        mSurfaceControl = viewRootImpl != null
-                ? viewRootImpl.getSurfaceControl()
-                : null;
-    }
-
-    public boolean isValid() {
-        return mSurfaceControl != null && mSurfaceControl.isValid();
-    }
-
-    public SurfaceControl getSurfaceControl() {
-        return mSurfaceControl;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index e281914..30c062b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -205,13 +205,6 @@
             /**
              * @param surface The surface to modify.
              */
-            public Builder(SurfaceControlCompat surface) {
-                this(surface.mSurfaceControl);
-            }
-
-            /**
-             * @param surface The surface to modify.
-             */
             public Builder(SurfaceControl surface) {
                 this.surface = surface;
             }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index c043fba..43a882a5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -35,70 +35,69 @@
         mTransaction.apply();
     }
 
-    public TransactionCompat show(SurfaceControlCompat surfaceControl) {
-        mTransaction.show(surfaceControl.mSurfaceControl);
+    public TransactionCompat show(SurfaceControl surfaceControl) {
+        mTransaction.show(surfaceControl);
         return this;
     }
 
-    public TransactionCompat hide(SurfaceControlCompat surfaceControl) {
-        mTransaction.hide(surfaceControl.mSurfaceControl);
+    public TransactionCompat hide(SurfaceControl surfaceControl) {
+        mTransaction.hide(surfaceControl);
         return this;
     }
 
-    public TransactionCompat setPosition(SurfaceControlCompat surfaceControl, float x, float y) {
-        mTransaction.setPosition(surfaceControl.mSurfaceControl, x, y);
+    public TransactionCompat setPosition(SurfaceControl surfaceControl, float x, float y) {
+        mTransaction.setPosition(surfaceControl, x, y);
         return this;
     }
 
-    public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
-        mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);
+    public TransactionCompat setSize(SurfaceControl surfaceControl, int w, int h) {
+        mTransaction.setBufferSize(surfaceControl, w, h);
         return this;
     }
 
-    public TransactionCompat setLayer(SurfaceControlCompat surfaceControl, int z) {
-        mTransaction.setLayer(surfaceControl.mSurfaceControl, z);
+    public TransactionCompat setLayer(SurfaceControl surfaceControl, int z) {
+        mTransaction.setLayer(surfaceControl, z);
         return this;
     }
 
-    public TransactionCompat setAlpha(SurfaceControlCompat surfaceControl, float alpha) {
-        mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);
+    public TransactionCompat setAlpha(SurfaceControl surfaceControl, float alpha) {
+        mTransaction.setAlpha(surfaceControl, alpha);
         return this;
     }
 
-    public TransactionCompat setOpaque(SurfaceControlCompat surfaceControl, boolean opaque) {
-        mTransaction.setOpaque(surfaceControl.mSurfaceControl, opaque);
+    public TransactionCompat setOpaque(SurfaceControl surfaceControl, boolean opaque) {
+        mTransaction.setOpaque(surfaceControl, opaque);
         return this;
     }
 
-    public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, float dsdx, float dtdx,
+    public TransactionCompat setMatrix(SurfaceControl surfaceControl, float dsdx, float dtdx,
             float dtdy, float dsdy) {
-        mTransaction.setMatrix(surfaceControl.mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
+        mTransaction.setMatrix(surfaceControl, dsdx, dtdx, dtdy, dsdy);
         return this;
     }
 
-    public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, Matrix matrix) {
-        mTransaction.setMatrix(surfaceControl.mSurfaceControl, matrix, mTmpValues);
+    public TransactionCompat setMatrix(SurfaceControl surfaceControl, Matrix matrix) {
+        mTransaction.setMatrix(surfaceControl, matrix, mTmpValues);
         return this;
     }
 
-    public TransactionCompat setWindowCrop(SurfaceControlCompat surfaceControl, Rect crop) {
-        mTransaction.setWindowCrop(surfaceControl.mSurfaceControl, crop);
+    public TransactionCompat setWindowCrop(SurfaceControl surfaceControl, Rect crop) {
+        mTransaction.setWindowCrop(surfaceControl, crop);
         return this;
     }
 
-    public TransactionCompat setCornerRadius(SurfaceControlCompat surfaceControl, float radius) {
-        mTransaction.setCornerRadius(surfaceControl.mSurfaceControl, radius);
+    public TransactionCompat setCornerRadius(SurfaceControl surfaceControl, float radius) {
+        mTransaction.setCornerRadius(surfaceControl, radius);
         return this;
     }
 
-    public TransactionCompat setBackgroundBlurRadius(SurfaceControlCompat surfaceControl,
-            int radius) {
-        mTransaction.setBackgroundBlurRadius(surfaceControl.mSurfaceControl, radius);
+    public TransactionCompat setBackgroundBlurRadius(SurfaceControl surfaceControl, int radius) {
+        mTransaction.setBackgroundBlurRadius(surfaceControl, radius);
         return this;
     }
 
-    public TransactionCompat setColor(SurfaceControlCompat surfaceControl, float[] color) {
-        mTransaction.setColor(surfaceControl.mSurfaceControl, color);
+    public TransactionCompat setColor(SurfaceControl surfaceControl, float[] color) {
+        mTransaction.setColor(surfaceControl, color);
         return this;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
new file mode 100644
index 0000000..ac62cf9
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.unfold
+
+import android.content.ContentResolver
+import android.content.Context
+import android.hardware.SensorManager
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
+import dagger.BindsInstance
+import dagger.Component
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Singleton
+
+/**
+ * Provides [UnfoldTransitionProgressProvider]. The [Optional] is empty when the transition
+ * animation is disabled.
+ *
+ * This component is meant to be used for places that don't use dagger. By providing those
+ * parameters to the factory, all dagger objects are correctly instantiated. See
+ * [createUnfoldTransitionProgressProvider] for an example.
+ */
+@Singleton
+@Component(modules = [UnfoldSharedModule::class])
+internal interface UnfoldSharedComponent {
+
+    @Component.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance context: Context,
+            @BindsInstance config: UnfoldTransitionConfig,
+            @BindsInstance screenStatusProvider: ScreenStatusProvider,
+            @BindsInstance deviceStateManager: DeviceStateManager,
+            @BindsInstance sensorManager: SensorManager,
+            @BindsInstance @Main handler: Handler,
+            @BindsInstance @Main executor: Executor,
+            @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
+            @BindsInstance contentResolver: ContentResolver = context.contentResolver
+        ): UnfoldSharedComponent
+    }
+
+    val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
new file mode 100644
index 0000000..23e4c97
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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.unfold
+
+import android.hardware.SensorManager
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
+import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import javax.inject.Singleton
+
+@Module
+class UnfoldSharedModule {
+    @Provides
+    @Singleton
+    fun unfoldTransitionProgressProvider(
+        config: UnfoldTransitionConfig,
+        scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+        tracingListener: ATraceLoggerTransitionProgressListener,
+        foldStateProvider: FoldStateProvider
+    ): Optional<UnfoldTransitionProgressProvider> =
+        if (!config.isEnabled) {
+            Optional.empty()
+        } else {
+            val baseProgressProvider =
+                if (config.isHingeAngleEnabled) {
+                    PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
+                } else {
+                    FixedTimingTransitionProgressProvider(foldStateProvider)
+                }
+            Optional.of(
+                scaleAwareProviderFactory.wrap(baseProgressProvider).apply {
+                    // Always present callback that logs animation beginning and end.
+                    addCallback(tracingListener)
+                })
+        }
+
+    @Provides
+    @Singleton
+    fun provideFoldStateProvider(
+        deviceFoldStateProvider: DeviceFoldStateProvider
+    ): FoldStateProvider = deviceFoldStateProvider
+
+    @Provides
+    fun hingeAngleProvider(
+        config: UnfoldTransitionConfig,
+        sensorManager: SensorManager
+    ): HingeAngleProvider =
+        if (config.isHingeAngleEnabled) {
+            HingeSensorAngleProvider(sensorManager)
+        } else {
+            EmptyHingeAngleProvider
+        }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 953b0e0..d5d6362 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -23,22 +23,16 @@
 import android.os.Handler
 import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
-import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
-import com.android.systemui.unfold.updates.DeviceFoldStateProvider
-import com.android.systemui.unfold.updates.FoldStateProvider
-import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
-import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
-import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
-import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
 import java.util.concurrent.Executor
 
 /**
  * Factory for [UnfoldTransitionProgressProvider].
  *
- * This is needed as Launcher has to create the object manually. Sysui create it using dagger (see
- * [UnfoldTransitionModule]).
+ * This is needed as Launcher has to create the object manually. If dagger is available, this object
+ * is provided in [UnfoldSharedModule].
+ *
+ * This should **never** be called from sysui, as the object is already provided in that process.
  */
 fun createUnfoldTransitionProgressProvider(
     context: Context,
@@ -49,62 +43,21 @@
     mainHandler: Handler,
     mainExecutor: Executor,
     tracingTagPrefix: String
-): UnfoldTransitionProgressProvider {
-
-    if (!config.isEnabled) {
-        throw IllegalStateException(
-            "Trying to create " +
-                "UnfoldTransitionProgressProvider when the transition is disabled")
-    }
-
-    val foldStateProvider =
-        createFoldStateProvider(
+): UnfoldTransitionProgressProvider =
+    DaggerUnfoldSharedComponent.factory()
+        .create(
             context,
             config,
             screenStatusProvider,
             deviceStateManager,
             sensorManager,
             mainHandler,
-            mainExecutor)
-
-    val unfoldTransitionProgressProvider =
-        if (config.isHingeAngleEnabled) {
-            PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
-        } else {
-            FixedTimingTransitionProgressProvider(foldStateProvider)
-        }
-
-    return ScaleAwareTransitionProgressProvider(
-            unfoldTransitionProgressProvider, context.contentResolver)
-        .apply {
-            // Always present callback that logs animation beginning and end.
-            addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
-        }
-}
-
-fun createFoldStateProvider(
-    context: Context,
-    config: UnfoldTransitionConfig,
-    screenStatusProvider: ScreenStatusProvider,
-    deviceStateManager: DeviceStateManager,
-    sensorManager: SensorManager,
-    mainHandler: Handler,
-    mainExecutor: Executor
-): FoldStateProvider {
-    val hingeAngleProvider =
-        if (config.isHingeAngleEnabled) {
-            HingeSensorAngleProvider(sensorManager)
-        } else {
-            EmptyHingeAngleProvider()
-        }
-
-    return DeviceFoldStateProvider(
-        context,
-        hingeAngleProvider,
-        screenStatusProvider,
-        deviceStateManager,
-        mainExecutor,
-        mainHandler)
-}
+            mainExecutor,
+            tracingTagPrefix)
+        .unfoldTransitionProvider
+        .orElse(null)
+        ?: throw IllegalStateException(
+            "Trying to create " +
+                "UnfoldTransitionProgressProvider when the transition is disabled")
 
 fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index cd1ea21..204ae09 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -22,6 +22,7 @@
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
@@ -29,14 +30,17 @@
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import java.util.concurrent.Executor
+import javax.inject.Inject
 
-class DeviceFoldStateProvider(
+class DeviceFoldStateProvider
+@Inject
+constructor(
     context: Context,
     private val hingeAngleProvider: HingeAngleProvider,
     private val screenStatusProvider: ScreenStatusProvider,
     private val deviceStateManager: DeviceStateManager,
-    private val mainExecutor: Executor,
-    private val handler: Handler
+    @Main private val mainExecutor: Executor,
+    @Main private val handler: Handler
 ) : FoldStateProvider {
 
     private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
index 9b58b1f..4ca1a53 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -2,7 +2,7 @@
 
 import androidx.core.util.Consumer
 
-internal class EmptyHingeAngleProvider : HingeAngleProvider {
+internal object EmptyHingeAngleProvider : HingeAngleProvider {
     override fun start() {
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
similarity index 76%
rename from packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
index f3eeb32..1574c8d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
@@ -2,6 +2,8 @@
 
 import android.os.Trace
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+import javax.inject.Qualifier
 
 /**
  * Listener that logs start and end of the fold-unfold transition.
@@ -9,7 +11,10 @@
  * [tracePrefix] arg helps in differentiating those. Currently, this is expected to be logged twice
  * for each fold/unfold: in (1) systemui and (2) launcher process.
  */
-class ATraceLoggerTransitionProgressListener(tracePrefix: String) : TransitionProgressListener {
+class ATraceLoggerTransitionProgressListener
+@Inject
+internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String) :
+    TransitionProgressListener {
 
     private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
 
@@ -27,3 +32,5 @@
 }
 
 private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
+
+@Qualifier annotation class UnfoldTransitionATracePrefix
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/JankMonitorTransitionProgressListener.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/JankMonitorTransitionProgressListener.kt
new file mode 100644
index 0000000..2b38f3d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/JankMonitorTransitionProgressListener.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.unfold.util
+
+import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.jank.InteractionJankMonitor.CUJ_UNFOLD_ANIM
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.util.function.Supplier
+
+class JankMonitorTransitionProgressListener(private val attachedViewProvider: Supplier<View>) :
+    TransitionProgressListener {
+
+    private val interactionJankMonitor = InteractionJankMonitor.getInstance()
+
+    override fun onTransitionStarted() {
+        interactionJankMonitor.begin(attachedViewProvider.get(), CUJ_UNFOLD_ANIM)
+    }
+
+    override fun onTransitionFinished() {
+        interactionJankMonitor.end(CUJ_UNFOLD_ANIM)
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index df9078a..ee79b87 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -6,15 +6,20 @@
 import android.provider.Settings
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
 /** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */
-class ScaleAwareTransitionProgressProvider(
-    unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+class ScaleAwareTransitionProgressProvider
+@AssistedInject
+constructor(
+    @Assisted progressProviderToWrap: UnfoldTransitionProgressProvider,
     private val contentResolver: ContentResolver
 ) : UnfoldTransitionProgressProvider {
 
     private val scopedUnfoldTransitionProgressProvider =
-            ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+        ScopedUnfoldTransitionProgressProvider(progressProviderToWrap)
 
     private val animatorDurationScaleObserver = object : ContentObserver(null) {
         override fun onChange(selfChange: Boolean) {
@@ -47,4 +52,11 @@
         contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
         scopedUnfoldTransitionProgressProvider.destroy()
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun wrap(
+            progressProvider: UnfoldTransitionProgressProvider
+        ): ScaleAwareTransitionProgressProvider
+    }
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
similarity index 70%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
index 6041460..dc804ca 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/packages/SystemUI/src-debug/com/android/systemui/util/Compile.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package com.android.systemui.util;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+/** Constants that vary by compilation configuration. */
+public class Compile {
+    /** Whether SystemUI was compiled in debug mode, and supports debug features */
+    public static final boolean IS_DEBUG = true;
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.aidl b/packages/SystemUI/src-release/com/android/systemui/util/Compile.java
similarity index 70%
copy from media/java/android/media/tv/interactive/TvIAppInfo.aidl
copy to packages/SystemUI/src-release/com/android/systemui/util/Compile.java
index 6041460..8a63763 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.aidl
+++ b/packages/SystemUI/src-release/com/android/systemui/util/Compile.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.tv.interactive;
+package com.android.systemui.util;
 
-parcelable TvIAppInfo;
\ No newline at end of file
+/** Constants that vary by compilation configuration. */
+public class Compile {
+    /** Whether SystemUI was compiled in debug mode, and supports debug features */
+    public static final boolean IS_DEBUG = false;
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 26c227d..8bcb7c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -91,7 +91,8 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_DISABLE_ESIM),
-                PERMISSION_SELF, null /* scheduler */);
+                PERMISSION_SELF, null /* scheduler */,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     public static boolean isEsimLocked(Context context, int subId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 099dd5d..75579b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -30,6 +30,8 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.policy.SystemBarUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
@@ -57,6 +59,11 @@
     private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
     private boolean mBouncerVisible;
     private boolean mAltBouncerShowing;
+    /**
+     * Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't
+     * contain {@link R.id.keyguard_message_area_container}.
+     */
+    @Nullable
     private ViewGroup mContainer;
     private int mTopMargin;
 
@@ -75,6 +82,9 @@
     }
 
     void onConfigChanged() {
+        if (mContainer == null) {
+            return;
+        }
         final int newTopMargin = SystemBarUtils.getStatusBarHeight(getContext());
         if (mTopMargin == newTopMargin) {
             return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5276679..a348b42 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3294,11 +3294,19 @@
     }
 
     public void clearBiometricRecognized() {
+        clearBiometricRecognized(UserHandle.USER_NULL);
+    }
+
+    public void clearBiometricRecognizedWhenKeyguardDone(int unlockedUser) {
+        clearBiometricRecognized(unlockedUser);
+    }
+
+    private void clearBiometricRecognized(int unlockedUser) {
         Assert.isMainThread();
         mUserFingerprintAuthenticated.clear();
         mUserFaceAuthenticated.clear();
-        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
-        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE);
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
+        mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
 
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index a100cb8..9c2971c 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -96,6 +96,8 @@
 import com.android.systemui.util.concurrency.ThreadFactory;
 import com.android.systemui.util.settings.SecureSettings;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -107,7 +109,7 @@
  * for antialiasing and emulation purposes.
  */
 @SysUISingleton
-public class ScreenDecorations extends CoreStartable implements Tunable {
+public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable{
     private static final boolean DEBUG = false;
     private static final String TAG = "ScreenDecorations";
 
@@ -677,6 +679,20 @@
         });
     }
 
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("ScreenDecorations state:");
+        pw.println("  DEBUG_DISABLE_SCREEN_DECORATIONS:" + DEBUG_DISABLE_SCREEN_DECORATIONS);
+        pw.println("  mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
+        pw.println("  mIsPrivacyDotEnabled:" + mIsPrivacyDotEnabled);
+        pw.println("  mPendingRotationChange:" + mPendingRotationChange);
+        pw.println("  mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
+        pw.println("  mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
+                + ")");
+        pw.println("  mRoundedDefaultBottom(x,y)=(" + mRoundedDefaultBottom.x + ","
+                + mRoundedDefaultBottom.y + ")");
+    }
+
     private void updateOrientation() {
         Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
                 "must call on " + mHandler.getLooper().getThread()
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index d7da63b..1f2de4c 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -112,7 +112,8 @@
 
         public void register(Context context, ComponentName receiver, IntentFilter filter) {
             mReceivers.add(receiver);
-            context.registerReceiver(this, filter);
+            context.registerReceiver(this, filter,
+                    Context.RECEIVER_EXPORTED_UNAUDITED);
         }
 
         public void unregister(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 63962fa..daca918 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -32,6 +32,9 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Dumpable;
+import android.util.DumpableContainer;
 import android.util.Log;
 import android.util.TimingsTraceLog;
 import android.view.SurfaceControl;
@@ -53,13 +56,19 @@
  * Application class for SystemUI.
  */
 public class SystemUIApplication extends Application implements
-        SystemUIAppComponentFactory.ContextInitializer {
+        SystemUIAppComponentFactory.ContextInitializer, DumpableContainer {
 
     public static final String TAG = "SystemUIService";
     private static final boolean DEBUG = false;
 
     private ContextComponentHelper mComponentHelper;
     private BootCompleteCacheImpl mBootCompleteCache;
+    private DumpManager mDumpManager;
+
+    /**
+     * Map of dumpables added externally.
+     */
+    private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
 
     /**
      * Hold a reference on the stuff we start.
@@ -214,7 +223,7 @@
             }
         }
 
-        final DumpManager dumpManager = mSysUIComponent.createDumpManager();
+        mDumpManager = mSysUIComponent.createDumpManager();
 
         Log.v(TAG, "Starting SystemUI services for user " +
                 Process.myUserHandle().getIdentifier() + ".");
@@ -255,7 +264,7 @@
                 mServices[i].onBootCompleted();
             }
 
-            dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
+            mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
         }
         mSysUIComponent.getInitController().executePostInitTasks();
         log.traceEnd();
@@ -263,6 +272,29 @@
         mServicesStarted = true;
     }
 
+    // TODO(b/149254050): add unit tests? There doesn't seem to be a SystemUiApplicationTest...
+    @Override
+    public boolean addDumpable(Dumpable dumpable) {
+        String name = dumpable.getDumpableName();
+        if (mDumpables.containsKey(name)) {
+            // This is normal because SystemUIApplication is an application context that is shared
+            // among multiple components
+            if (DEBUG) {
+                Log.d(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
+                        + " with that name (" + name + "): " + mDumpables.get(name));
+            }
+            return false;
+        }
+        if (DEBUG) Log.d(TAG, "addDumpable(): adding '" + name + "' = " + dumpable);
+        mDumpables.put(name, dumpable);
+
+        // TODO(b/149254050): replace com.android.systemui.dump.Dumpable by
+        // com.android.util.Dumpable and get rid of the intermediate lambda
+        mDumpManager.registerDumpable(dumpable.getDumpableName(),
+                (fd, pw, args) -> dumpable.dump(pw, args));
+        return true;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         if (mServicesStarted) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 1ac9016..2b12f67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -518,7 +518,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
 
-        context.registerReceiver(mBroadcastReceiver, filter);
+        context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index d208441..6581490 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -539,7 +539,8 @@
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        context.registerReceiver(mBroadcastReceiver, filter);
+        context.registerReceiver(mBroadcastReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         udfpsHapticsSimulator.setUdfpsController(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 572bb44..5b46079 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.dreams;
 
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Handler;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -26,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.dagger.DreamOverlayModule;
 import com.android.systemui.util.ViewController;
@@ -47,6 +51,15 @@
     // the space into which widgets are placed.
     private final ViewGroup mDreamOverlayContentView;
 
+    // The maximum translation offset to apply to the overlay container to avoid screen burn-in.
+    private final int mMaxBurnInOffset;
+
+    // The interval in milliseconds between burn-in protection updates.
+    private final long mBurnInProtectionUpdateInterval;
+
+    // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
+    private final Handler mHandler;
+
     // A hook into the internal inset calculation where we declare the overlays as the only
     // touchable regions.
     private final ViewTreeObserver.OnComputeInternalInsetsListener
@@ -81,13 +94,21 @@
     public DreamOverlayContainerViewController(
             DreamOverlayContainerView containerView,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
-            DreamOverlayStatusBarViewController statusBarViewController) {
+            DreamOverlayStatusBarViewController statusBarViewController,
+            @Main Handler handler,
+            @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
+            @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
+                    burnInProtectionUpdateInterval) {
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
         mDreamOverlayNotificationsDragAreaHeight =
                 mView.getResources().getDimensionPixelSize(
                         R.dimen.dream_overlay_notifications_drag_area_height);
+
+        mHandler = handler;
+        mMaxBurnInOffset = maxBurnInOffset;
+        mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
     }
 
     @Override
@@ -99,10 +120,12 @@
     protected void onViewAttached() {
         mView.getViewTreeObserver()
                 .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
     }
 
     @Override
     protected void onViewDetached() {
+        mHandler.removeCallbacks(this::updateBurnInOffsets);
         mView.getViewTreeObserver()
                 .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
     }
@@ -123,4 +146,15 @@
     int getDreamOverlayNotificationsDragAreaHeight() {
         return mDreamOverlayNotificationsDragAreaHeight;
     }
+
+    private void updateBurnInOffsets() {
+        // These translation values change slowly, and the set translation methods are idempotent,
+        // so no translation occurs when the values don't change.
+        mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
+                - mMaxBurnInOffset);
+        mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
+                - mMaxBurnInOffset);
+
+        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 5b588a9..d291203 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams.dagger;
 
 import android.content.ContentResolver;
+import android.content.res.Resources;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
@@ -45,6 +46,9 @@
     public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
             "dream_overlay_battery_controller";
     public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
+    public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
+    public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
+            "burn_in_protection_update_interval";
 
     /** */
     @Provides
@@ -104,4 +108,20 @@
                 contentResolver,
                 batteryController);
     }
+
+    /** */
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    @Named(MAX_BURN_IN_OFFSET)
+    static int providesMaxBurnInOffset(@Main Resources resources) {
+        return resources.getDimensionPixelSize(R.dimen.default_burn_in_prevention_offset);
+    }
+
+    /** */
+    @Provides
+    @Named(BURN_IN_PROTECTION_UPDATE_INTERVAL)
+    static long providesBurnInProtectionUpdateInterval(@Main Resources resources) {
+        return resources.getInteger(
+                R.integer.config_dreamOverlayBurnInProtectionUpdateIntervalMillis);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 89623f4..f3b721c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -88,7 +88,8 @@
         filter.addAction(ACTION_GET_FLAGS);
         flagManager.setRestartAction(this::restartSystemUI);
         flagManager.setClearCacheAction(this::removeFromCache);
-        context.registerReceiver(mReceiver, filter, null, null);
+        context.registerReceiver(mReceiver, filter, null, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         dumpManager.registerDumpable(TAG, this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index c46ffa0..b24d08d 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcelable;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -224,10 +225,12 @@
     }
 
     public void reloadFragments() {
+        Trace.beginSection("FrargmentHostManager#reloadFragments");
         // Save the old state.
         Parcelable p = destroyFragmentHost();
         // Generate a new fragment host and restore its state.
         createFragmentHost(p);
+        Trace.endSection();
     }
 
     class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 5ca2539..8376681 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -894,7 +894,8 @@
         delayedActionFilter.addAction(DELAYED_KEYGUARD_ACTION);
         delayedActionFilter.addAction(DELAYED_LOCK_PROFILE_ACTION);
         mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
-                SYSTEMUI_PERMISSION, null /* scheduler */);
+                SYSTEMUI_PERMISSION, null /* scheduler */,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
@@ -1760,7 +1761,7 @@
         }
     };
 
-    public void keyguardDone() {
+    private void keyguardDone() {
         Trace.beginSection("KeyguardViewMediator#keyguardDone");
         if (DEBUG) Log.d(TAG, "keyguardDone()");
         userActivity();
@@ -1894,9 +1895,8 @@
             resetKeyguardDonePendingLocked();
         }
 
-
         if (mGoingToSleep) {
-            mUpdateMonitor.clearBiometricRecognized();
+            mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser);
             Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
             return;
         }
@@ -1917,7 +1917,7 @@
         }
 
         handleHide();
-        mUpdateMonitor.clearBiometricRecognized();
+        mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser);
         Trace.endSection();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 2e1c9fa..474a81b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -138,6 +138,7 @@
         }
         setWakefulness(WAKEFULNESS_AWAKE);
         dispatch(Observer::onFinishedWakingUp);
+        dispatch(Observer::onPostFinishedWakingUp);
     }
 
     public void dispatchStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
@@ -236,6 +237,12 @@
     public interface Observer {
         default void onStartedWakingUp() {}
         default void onFinishedWakingUp() {}
+
+        /**
+         * Called after the finished waking up call, ensuring it's after all the other listeners,
+         * reacting to {@link #onFinishedWakingUp()}
+         */
+        default void onPostFinishedWakingUp() {}
         default void onStartedGoingToSleep() {}
         default void onFinishedGoingToSleep() {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 558f0e6..d1fe7d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media.dagger;
 
+import android.app.Service;
 import android.content.Context;
 import android.view.WindowManager;
 
@@ -30,6 +31,7 @@
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
 import com.android.systemui.statusbar.commandline.CommandRegistry;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -38,8 +40,11 @@
 
 import javax.inject.Named;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 /** Dagger module for the media package. */
 @Module
@@ -128,4 +133,10 @@
                         mediaTttChipControllerReceiver,
                         mainExecutor));
     }
+
+    /** Inject into MediaTttSenderService. */
+    @Binds
+    @IntoMap
+    @ClassKey(MediaTttSenderService.class)
+    Service bindMediaTttSenderService(MediaTttSenderService service);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 5a86723..460d38f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,9 +16,14 @@
 
 package com.android.systemui.media.taptotransfer
 
+import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
 import android.graphics.Color
 import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.os.IBinder
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.R
@@ -27,9 +32,12 @@
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
+import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
 import com.android.systemui.media.taptotransfer.sender.TransferInitiated
 import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -44,11 +52,14 @@
 @SysUISingleton
 class MediaTttCommandLineHelper @Inject constructor(
     commandRegistry: CommandRegistry,
-    context: Context,
+    private val context: Context,
     private val mediaTttChipControllerSender: MediaTttChipControllerSender,
     private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
     @Main private val mainExecutor: DelayableExecutor,
 ) {
+    private var senderCallback: IDeviceSenderCallback? = null
+    private val senderServiceConnection = SenderServiceConnection()
+
     private val appIconDrawable =
         Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
             it.setTint(Color.YELLOW)
@@ -68,14 +79,20 @@
     inner class AddChipCommandSender : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
             val otherDeviceName = args[0]
+            val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
+                .addFeature("feature")
+                .build()
+            val otherDeviceInfo = DeviceInfo(otherDeviceName)
+
             when (args[1]) {
-                MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
-                    mediaTttChipControllerSender.displayChip(
-                        MoveCloserToTransfer(
-                            appIconDrawable, APP_ICON_CONTENT_DESCRIPTION, otherDeviceName
-                        )
-                    )
+                MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
+                    runOnService { senderCallback ->
+                        senderCallback.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
+                    }
                 }
+
+                // TODO(b/203800643): Migrate other commands to invoke the service instead of the
+                //   controller.
                 TRANSFER_INITIATED_COMMAND_NAME -> {
                     val futureTask = FutureTask { fakeUndoRunnable }
                     mediaTttChipControllerSender.displayChip(
@@ -101,7 +118,7 @@
                 }
                 else -> {
                     pw.println("Chip type must be one of " +
-                            "$MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME, " +
+                            "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
                             "$TRANSFER_INITIATED_COMMAND_NAME, " +
                             TRANSFER_SUCCEEDED_COMMAND_NAME
                     )
@@ -114,19 +131,40 @@
                     "$ADD_CHIP_COMMAND_SENDER_TAG <deviceName> <chipStatus>"
             )
         }
+
+        private fun runOnService(command: SenderCallbackCommand) {
+            val currentServiceCallback = senderCallback
+            if (currentServiceCallback != null) {
+                command.run(currentServiceCallback)
+            } else {
+                bindService(command)
+            }
+        }
+
+        private fun bindService(command: SenderCallbackCommand) {
+            senderServiceConnection.pendingCommand = command
+            val binding = context.bindService(
+                Intent(context, MediaTttSenderService::class.java),
+                senderServiceConnection,
+                Context.BIND_AUTO_CREATE
+            )
+            Log.i(TAG, "Starting service binding? $binding")
+        }
     }
 
     /** A command to REMOVE the media ttt chip on the SENDER device. */
     inner class RemoveChipCommandSender : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
             mediaTttChipControllerSender.removeChip()
+            if (senderCallback != null) {
+                context.unbindService(senderServiceConnection)
+            }
         }
         override fun help(pw: PrintWriter) {
             pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_SENDER_TAG")
         }
     }
 
-
     /** A command to DISPLAY the media ttt chip on the RECEIVER device. */
     inner class AddChipCommandReceiver : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
@@ -149,6 +187,29 @@
         }
     }
 
+    /** A service connection for [IDeviceSenderCallback]. */
+    private inner class SenderServiceConnection : ServiceConnection {
+        // A command that should be run when the service gets connected.
+        var pendingCommand: SenderCallbackCommand? = null
+
+        override fun onServiceConnected(className: ComponentName, service: IBinder) {
+            val newCallback = IDeviceSenderCallback.Stub.asInterface(service)
+            senderCallback = newCallback
+            pendingCommand?.run(newCallback)
+            pendingCommand = null
+        }
+
+        override fun onServiceDisconnected(className: ComponentName) {
+            senderCallback = null
+        }
+    }
+
+    /** An interface defining a command that should be run on the sender callback. */
+    private fun interface SenderCallbackCommand {
+        /** Runs the command on the provided [senderCallback]. */
+        fun run(senderCallback: IDeviceSenderCallback)
+    }
+
     private val fakeUndoRunnable = Runnable {
         Log.i(TAG, "Undo runnable triggered")
     }
@@ -163,7 +224,7 @@
 @VisibleForTesting
 const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
 @VisibleForTesting
-val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleName!!
+val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!!
 @VisibleForTesting
 val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
 @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index b1f6faa..dd434e7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -40,17 +40,18 @@
 ) : MediaTttChipState(appIconDrawable, appIconContentDescription)
 
 /**
- * A state representing that the two devices are close but not close enough to initiate a transfer.
- * The chip will instruct the user to move closer in order to initiate the transfer.
+ * A state representing that the two devices are close but not close enough to *start* a cast to
+ * the receiver device. The chip will instruct the user to move closer in order to initiate the
+ * transfer to the receiver.
  */
-class MoveCloserToTransfer(
+class MoveCloserToStartCast(
     appIconDrawable: Drawable,
     appIconContentDescription: String,
     otherDeviceName: String,
 ) : ChipStateSender(
     appIconDrawable,
     appIconContentDescription,
-    R.string.media_move_closer_to_transfer,
+    R.string.media_move_closer_to_start_cast,
     otherDeviceName
 )
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
new file mode 100644
index 0000000..b56a699
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.media.taptotransfer.sender
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.os.IBinder
+import com.android.systemui.R
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import javax.inject.Inject
+
+/**
+ * Service that allows external handlers to trigger the media chip on the sender device.
+ */
+class MediaTttSenderService @Inject constructor(
+    context: Context,
+    val controller: MediaTttChipControllerSender
+) : Service() {
+
+    // TODO(b/203800643): Add logging when callbacks trigger.
+    private val binder: IBinder = object : IDeviceSenderCallback.Stub() {
+        override fun closeToReceiverToStartCast(
+            mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+        ) {
+            this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
+        }
+    }
+
+    // TODO(b/203800643): Use the app icon from the media info instead of a fake one.
+    private val fakeAppIconDrawable =
+        Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
+            it.setTint(Color.YELLOW)
+        }
+
+    override fun onBind(intent: Intent?): IBinder = binder
+
+    private fun closeToReceiverToStartCast(
+        mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+    ) {
+        val chipState = MoveCloserToStartCast(
+            appIconDrawable = fakeAppIconDrawable,
+            appIconContentDescription = mediaInfo.name.toString(),
+            otherDeviceName = otherDeviceInfo.name
+        )
+        controller.displayChip(chipState)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 41dced6..259b786 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -25,6 +25,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Trace;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
@@ -172,9 +173,14 @@
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
-                R.style.Theme_SystemUI_QuickSettings));
-        return inflater.inflate(R.layout.qs_panel, container, false);
+        try {
+            Trace.beginSection("QSFragment#onCreateView");
+            inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+                    R.style.Theme_SystemUI_QuickSettings));
+            return inflater.inflate(R.layout.qs_panel, container, false);
+        } finally {
+            Trace.endSection();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 866b1b8..d3f8db38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -247,10 +247,9 @@
         boolean shouldUseSplitShade =
                 resources.getBoolean(R.bool.config_use_split_notification_shade);
 
-        mStatusIconsView.setVisibility(
-                shouldUseSplitShade || mUseCombinedQSHeader ? View.GONE : View.VISIBLE);
-        mDatePrivacyView.setVisibility(
-                shouldUseSplitShade || mUseCombinedQSHeader ? View.GONE : View.VISIBLE);
+        boolean gone = shouldUseSplitShade || mUseCombinedQSHeader || mQsDisabled;
+        mStatusIconsView.setVisibility(gone ? View.GONE : View.VISIBLE);
+        mDatePrivacyView.setVisibility(gone ? View.GONE : View.VISIBLE);
 
         mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 9acd3eb..d3bad16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -414,9 +414,9 @@
                 public void onLayoutChange(View v, int left, int top, int right, int bottom,
                         int oldLeft, int oldTop, int oldRight, int oldBottom) {
                     holder.mTileView.removeOnLayoutChangeListener(this);
-                    holder.mTileView.requestFocus();
+                    holder.mTileView.requestAccessibilityFocus();
                     if (mAccessibilityAction == ACTION_NONE) {
-                        holder.mTileView.clearFocus();
+                        holder.mTileView.clearAccessibilityFocus();
                     }
                 }
             });
@@ -449,12 +449,13 @@
         // Update the tile divider position
         mTileDividerIndex++;
         mFocusIndex = mEditIndex - 1;
+        final int focus = mFocusIndex;
         mNeedsFocus = true;
         if (mRecyclerView != null) {
             mRecyclerView.post(() -> {
                 final RecyclerView recyclerView = mRecyclerView;
                 if (recyclerView != null) {
-                    recyclerView.smoothScrollToPosition(mFocusIndex);
+                    recyclerView.smoothScrollToPosition(focus);
                 }
             });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 648e14c..c136d9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -7,7 +7,6 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.os.SystemClock
-import android.util.DisplayMetrics
 import android.util.IndentingPrintWriter
 import android.util.MathUtils
 import android.view.MotionEvent
@@ -24,6 +23,7 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.media.MediaHierarchyManager
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.plugins.FalsingManager
@@ -64,6 +64,7 @@
     private val scrimController: ScrimController,
     private val depthController: NotificationShadeDepthController,
     private val context: Context,
+    wakefulnessLifecycle: WakefulnessLifecycle,
     configurationController: ConfigurationController,
     falsingManager: FalsingManager,
     dumpManager: DumpManager,
@@ -120,6 +121,12 @@
     private var nextHideKeyguardNeedsNoAnimation = false
 
     /**
+     * Are we currently waking up to the shade locked
+     */
+    var isWakingToShadeLocked: Boolean = false
+        private set
+
+    /**
      * The distance until we're showing the notifications when pulsing
      */
     val distanceUntilShowingPulsingNotifications
@@ -160,6 +167,13 @@
                 }
             }
         })
+        wakefulnessLifecycle.addObserver(object : WakefulnessLifecycle.Observer {
+            override fun onPostFinishedWakingUp() {
+                // when finishing waking up, the UnlockedScreenOffAnimation has another attempt
+                // to reset keyguard. Let's do it in post
+                isWakingToShadeLocked = false
+            }
+        })
     }
 
     private fun updateResources() {
@@ -494,6 +508,10 @@
             draggedDownEntry = entry
         } else {
             logger.logGoingToLockedShade(animationHandler != null)
+            if (statusBarStateController.isDozing) {
+                // Make sure we don't go back to keyguard immediately again after waking up
+                isWakingToShadeLocked = true
+            }
             statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
             // This call needs to be after updating the shade state since otherwise
             // the scrimstate resets too early
@@ -605,6 +623,7 @@
             it.println("qSDragProgress: $qSDragProgress")
             it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
             it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
+            it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
             it.println("hasPendingHandlerOnKeyguardDismiss: " +
                 "${animationHandlerOnKeyguardDismiss != null}")
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 9dc823b..d785059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -293,7 +293,8 @@
 
         IntentFilter internalFilter = new IntentFilter();
         internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
-        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         mCurrentUserId = ActivityManager.getCurrentUser(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index ea51bd8..3fe108f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -107,8 +107,6 @@
     private var mDraggedFarEnough: Boolean = false
     private var mStartingChild: ExpandableView? = null
     private var mPulsing: Boolean = false
-    var isWakingToShadeLocked: Boolean = false
-        private set
 
     private var velocityTracker: VelocityTracker? = null
 
@@ -235,7 +233,6 @@
             mStartingChild = null
         }
         if (statusBarStateController.isDozing) {
-            isWakingToShadeLocked = true
             wakeUpCoordinator.willWakeUp = true
             mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
                     "com.android.systemui:PULSEDRAG")
@@ -333,10 +330,6 @@
         mPulsing = pulsing
     }
 
-    fun onStartedWakingUp() {
-        isWakingToShadeLocked = false
-    }
-
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         IndentingPrintWriter(pw, "  ").let {
             it.println("PulseExpansionHandler:")
@@ -344,7 +337,6 @@
             it.println("isExpanding: $isExpanding")
             it.println("leavingLockscreen: $leavingLockscreen")
             it.println("mPulsing: $mPulsing")
-            it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
             it.println("qsExpanded: $qsExpanded")
             it.println("bouncerShowing: $bouncerShowing")
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 5272a16..300c3a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -334,6 +334,8 @@
                 setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup());
             }
         });
+        // Get initial user setup state
+        setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup());
 
         WifiManager.ScanResultsCallback scanResultsCallback =
                 new WifiManager.ScanResultsCallback() {
@@ -994,6 +996,11 @@
     }
 
     @VisibleForTesting
+    boolean isUserSetup() {
+        return mUserSetup;
+    }
+
+    @VisibleForTesting
     boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
         if (allSubscriptions.size() != mMobileSignalControllers.size()) {
             return false;
@@ -1137,6 +1144,7 @@
     /** */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NetworkController state:");
+        pw.println("  mUserSetup=" + mUserSetup);
 
         pw.println("  - telephony ------");
         pw.print("  hasVoiceCallingFeature()=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index b0d41f1..3449bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -32,6 +32,8 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
@@ -132,12 +134,15 @@
 /**
  * Tracks state related to conversation notifications, and updates the UI of existing notifications
  * when necessary.
+ * TODO(b/214083332) Refactor this class to use the right coordinators and controllers
  */
 @SysUISingleton
 class ConversationNotificationManager @Inject constructor(
     private val notificationEntryManager: NotificationEntryManager,
     private val notificationGroupManager: NotificationGroupManagerLegacy,
     private val context: Context,
+    private val notifCollection: CommonNotifCollection,
+    private val featureFlags: NotifPipelineFlags,
     @Main private val mainHandler: Handler
 ) {
     // Need this state to be thread safe, since it's accessed from the ui thread
@@ -146,76 +151,93 @@
 
     private var notifPanelCollapsed = true
 
+    private val entryManagerListener = object : NotificationEntryListener {
+        override fun onNotificationRankingUpdated(rankingMap: RankingMap) =
+                updateNotificationRanking(rankingMap)
+        override fun onEntryInflated(entry: NotificationEntry) =
+                onEntryViewBound(entry)
+        override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
+        override fun onEntryRemoved(
+            entry: NotificationEntry,
+            visibility: NotificationVisibility?,
+            removedByUser: Boolean,
+            reason: Int
+        ) = removeTrackedEntry(entry)
+    }
+
+    private val notifCollectionListener = object : NotifCollectionListener {
+        override fun onRankingUpdate(ranking: RankingMap) =
+                updateNotificationRanking(ranking)
+
+        override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+            removeTrackedEntry(entry)
+        }
+    }
+
+    private fun updateNotificationRanking(rankingMap: RankingMap) {
+        fun getLayouts(view: NotificationContentView) =
+                sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
+        val ranking = Ranking()
+        val activeConversationEntries = states.keys.asSequence()
+                .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) }
+        for (entry in activeConversationEntries) {
+            if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
+                val important = ranking.channel.isImportantConversation
+                var changed = false
+                entry.row?.layouts?.asSequence()
+                        ?.flatMap(::getLayouts)
+                        ?.mapNotNull { it as? ConversationLayout }
+                        ?.filterNot { it.isImportantConversation == important }
+                        ?.forEach { layout ->
+                            changed = true
+                            if (important && entry.isMarkedForUserTriggeredMovement) {
+                                // delay this so that it doesn't animate in until after
+                                // the notif has been moved in the shade
+                                mainHandler.postDelayed(
+                                        {
+                                            layout.setIsImportantConversation(
+                                                    important,
+                                                    true)
+                                        },
+                                        IMPORTANCE_ANIMATION_DELAY.toLong())
+                            } else {
+                                layout.setIsImportantConversation(important, false)
+                            }
+                        }
+                if (changed) {
+                    notificationGroupManager.updateIsolation(entry)
+                }
+            }
+        }
+    }
+    fun onEntryViewBound(entry: NotificationEntry) {
+        if (!entry.ranking.isConversation) {
+            return
+        }
+        fun updateCount(isExpanded: Boolean) {
+            if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
+                resetCount(entry.key)
+                entry.row?.let(::resetBadgeUi)
+            }
+        }
+        entry.row?.setOnExpansionChangedListener { isExpanded ->
+            if (entry.row?.isShown == true && isExpanded) {
+                entry.row.performOnIntrinsicHeightReached {
+                    updateCount(isExpanded)
+                }
+            } else {
+                updateCount(isExpanded)
+            }
+        }
+        updateCount(entry.row?.isExpanded == true)
+    }
+
     init {
-        notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
-            override fun onNotificationRankingUpdated(rankingMap: RankingMap) {
-                fun getLayouts(view: NotificationContentView) =
-                        sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
-                val ranking = Ranking()
-                val activeConversationEntries = states.keys.asSequence()
-                        .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) }
-                for (entry in activeConversationEntries) {
-                    if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
-                        val important = ranking.channel.isImportantConversation
-                        var changed = false
-                        entry.row?.layouts?.asSequence()
-                                ?.flatMap(::getLayouts)
-                                ?.mapNotNull { it as? ConversationLayout }
-                                ?.filterNot { it.isImportantConversation == important }
-                                ?.forEach { layout ->
-                                    changed = true
-                                    if (important && entry.isMarkedForUserTriggeredMovement) {
-                                        // delay this so that it doesn't animate in until after
-                                        // the notif has been moved in the shade
-                                        mainHandler.postDelayed(
-                                                {
-                                                    layout.setIsImportantConversation(
-                                                            important,
-                                                            true)
-                                                },
-                                                IMPORTANCE_ANIMATION_DELAY.toLong())
-                                    } else {
-                                        layout.setIsImportantConversation(important, false)
-                                    }
-                                }
-                        if (changed) {
-                            notificationGroupManager.updateIsolation(entry)
-                        }
-                    }
-                }
-            }
-
-            override fun onEntryInflated(entry: NotificationEntry) {
-                if (!entry.ranking.isConversation) {
-                    return
-                }
-                fun updateCount(isExpanded: Boolean) {
-                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
-                        resetCount(entry.key)
-                        entry.row?.let(::resetBadgeUi)
-                    }
-                }
-                entry.row?.setOnExpansionChangedListener { isExpanded ->
-                    if (entry.row?.isShown == true && isExpanded) {
-                        entry.row.performOnIntrinsicHeightReached {
-                            updateCount(isExpanded)
-                        }
-                    } else {
-                        updateCount(isExpanded)
-                    }
-                }
-                updateCount(entry.row?.isExpanded == true)
-            }
-
-            override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
-
-            override fun onEntryRemoved(
-                entry: NotificationEntry,
-                visibility: NotificationVisibility?,
-                removedByUser: Boolean,
-                reason: Int
-            ) = removeTrackedEntry(entry)
-        })
+        if (featureFlags.isNewPipelineEnabled()) {
+            notifCollection.addCollectionListener(notifCollectionListener)
+        } else {
+            notificationEntryManager.addNotificationEntryListener(entryManagerListener)
+        }
     }
 
     private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 09c608d..062e239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.Compile;
 import com.android.systemui.util.leak.LeakDetector;
 
 import java.io.FileDescriptor;
@@ -1011,7 +1012,7 @@
     }
 
     private static final String TAG = "NotificationEntryMgr";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * Used when a notification is removed and it doesn't have a reason that maps to one of the
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 b6d2150..7605561 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
@@ -56,7 +56,6 @@
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.Pair;
-import android.util.Slog;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -756,6 +755,7 @@
                 && !entry.getSbn().getNotification().isGroupSummary()
                 && !hasFlag(entry, Notification.FLAG_ONGOING_EVENT)
                 && !hasFlag(entry, Notification.FLAG_BUBBLE)
+                && !hasFlag(entry, Notification.FLAG_NO_CLEAR)
                 && entry.getDismissState() != DISMISSED;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ec4e039..195f367 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -31,6 +31,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -98,6 +99,7 @@
 
     /** How long we can delay a group while waiting for all children to inflate */
     private final long mMaxGroupInflationDelay;
+    private final ConversationNotificationManager mConversationManager;
 
     @Inject
     public PreparationCoordinator(
@@ -106,7 +108,8 @@
             NotifInflationErrorManager errorManager,
             NotifViewBarn viewBarn,
             NotifUiAdjustmentProvider adjustmentProvider,
-            IStatusBarService service) {
+            IStatusBarService service,
+            ConversationNotificationManager conversationManager) {
         this(
                 logger,
                 notifInflater,
@@ -114,6 +117,7 @@
                 viewBarn,
                 adjustmentProvider,
                 service,
+                conversationManager,
                 CHILD_BIND_CUTOFF,
                 MAX_GROUP_INFLATION_DELAY);
     }
@@ -126,6 +130,7 @@
             NotifViewBarn viewBarn,
             NotifUiAdjustmentProvider adjustmentProvider,
             IStatusBarService service,
+            ConversationNotificationManager conversationManager,
             int childBindCutoff,
             long maxGroupInflationDelay) {
         mLogger = logger;
@@ -136,6 +141,7 @@
         mStatusBarService = service;
         mChildBindCutoff = childBindCutoff;
         mMaxGroupInflationDelay = maxGroupInflationDelay;
+        mConversationManager = conversationManager;
     }
 
     @Override
@@ -363,6 +369,9 @@
         mInflatingNotifs.remove(entry);
         mViewBarn.registerViewForEntry(entry, controller);
         mInflationStates.put(entry, STATE_INFLATED);
+        // NOTE: under the new pipeline there's no way to register for an inflation callback,
+        // so this one method is called by the PreparationCoordinator directly.
+        mConversationManager.onEntryViewBound(entry);
         mNotifInflatingFilter.invalidateList();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 5993f1d..3b93020 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -34,9 +34,9 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Compile;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.io.FileDescriptor;
@@ -69,8 +69,8 @@
         Dumpable {
 
     private static final String TAG = "NotifGroupManager";
-    private static final boolean DEBUG = StatusBar.DEBUG;
-    private static final boolean SPEW = StatusBar.SPEW;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
     /**
      * The maximum amount of time (in ms) between the posting of notifications that can be
      * considered part of the same update batch.
@@ -384,9 +384,9 @@
         // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
         // * Only necessary when at least one notification in the group is on a priority channel
         if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
-                != Notification.GROUP_ALERT_SUMMARY) {
+                == Notification.GROUP_ALERT_CHILDREN) {
             if (SPEW) {
-                Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+                Log.d(TAG, "getPriorityConversationAlertOverride: summary == GROUP_ALERT_CHILDREN");
             }
             return null;
         }
@@ -529,8 +529,10 @@
             mIsolatedEntries.put(entry.getKey(), entry.getSbn());
             if (groupKeysChanged) {
                 updateSuppression(mGroupMap.get(oldGroupKey));
-                updateSuppression(mGroupMap.get(newGroupKey));
             }
+            // Always update the suppression of the group from which you're isolated, in case
+            // this entry was or now is the alertOverride for that group.
+            updateSuppression(mGroupMap.get(newGroupKey));
         } else if (!wasGroupChild && isGroupChild) {
             onEntryBecomingChild(entry);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 433d5e1..2b4bc91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Compile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,8 +53,8 @@
 @SysUISingleton
 public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
     private static final String TAG = "InterruptionStateProvider";
-    private static final boolean DEBUG = true; //false;
-    private static final boolean DEBUG_HEADS_UP = true;
+    private static final boolean DEBUG = Compile.IS_DEBUG;
+    private static final boolean DEBUG_HEADS_UP = Compile.IS_DEBUG;
     private static final boolean ENABLE_HEADS_UP = true;
     private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9e8200b..dc39413 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.util.Compile;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -65,7 +66,7 @@
  */
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     /** The minimum delay in ms between reports of notification visibility. */
     private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 08a230b..dbd22db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -115,6 +115,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.Compile;
 import com.android.systemui.util.DumpUtilsKt;
 import com.android.systemui.wmshell.BubblesManager;
 
@@ -136,11 +137,11 @@
         implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
         NotificationFadeAware.FadeOptimizedNotification {
 
-    private static final boolean DEBUG = false;
+    private static final String TAG = "ExpandableNotifRow";
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
     private static final int MENU_VIEW_INDEX = 0;
-    private static final String TAG = "ExpandableNotifRow";
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
     private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index c0bafb7..4893490 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -48,11 +48,12 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.Compile;
 
 public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
 
     private static final String TAG = "FeedbackInfo";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     private NotificationGuts mGutsContainer;
     private NotificationListenerService.Ranking mRanking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index ab78d19..6abfee9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+import com.android.systemui.util.Compile;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -43,8 +44,9 @@
  */
 public class NotificationBlockingHelperManager {
     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
-    private static final boolean DEBUG = false;
     private static final String TAG = "BlockingHelper";
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG_ALWAYS_SHOW = false;
 
     private final Context mContext;
     private final NotificationGutsManager mNotificationGutsManager;
@@ -98,7 +100,7 @@
         // - The dismissed row is a valid group (>1 or 0 children from the same channel)
         // or the only child in the group
         final NotificationEntry entry = row.getEntry();
-        if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
+        if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG_ALWAYS_SHOW)
                 && mIsShadeExpanded
                 && !row.getIsNonblockable()
                 && ((!row.isChildInGroup() || mGroupMembershipManager.isOnlyChildInGroup(entry))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 4dec1f1..9cb5dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -61,6 +61,7 @@
 import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
+import com.android.systemui.util.Compile;
 import com.android.systemui.wmshell.BubblesManager;
 
 import java.io.FileDescriptor;
@@ -77,7 +78,7 @@
 public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
 
     private static final String TAG = "NotificationContentView";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
     public static final int VISIBLE_TYPE_CONTRACTED = 0;
     public static final int VISIBLE_TYPE_EXPANDED = 1;
     public static final int VISIBLE_TYPE_HEADSUP = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 9d599cb..d0fb416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -57,9 +57,6 @@
 public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
         ExpandableNotificationRow.LayoutListener {
 
-    private static final boolean DEBUG = false;
-    private static final String TAG = "swipe";
-
     // Notification must be swiped at least this fraction of a single menu item to show menu
     private static final float SWIPED_FAR_ENOUGH_MENU_FRACTION = 0.25f;
     private static final float SWIPED_FAR_ENOUGH_MENU_UNCLEARABLE_FRACTION = 0.15f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 915a85d..90f5179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -132,6 +132,7 @@
 
     public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
     private static final String TAG = "StackScroller";
+    private static final boolean SPEW = Log.isLoggable(TAG, Log.VERBOSE);
 
     // Delay in milli-seconds before shade closes for clear all.
     private final int DELAY_BEFORE_SHADE_CLOSE = 200;
@@ -3143,6 +3144,13 @@
             AnimationEvent event = new AnimationEvent(row, type);
             event.headsUpFromBottom = onBottom;
             mAnimationEvents.add(event);
+            if (SPEW) {
+                Log.v(TAG, "Generating HUN animation event: "
+                        + " isHeadsUp=" + isHeadsUp
+                        + " type=" + type
+                        + " onBottom=" + onBottom
+                        + " row=" + row.getEntry().getKey());
+            }
         }
         mHeadsUpChangeAnimations.clear();
         mAddedHeadsUpChildren.clear();
@@ -4677,7 +4685,22 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
-        if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
+        final boolean add = mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed);
+        if (SPEW) {
+            Log.v(TAG, "generateHeadsUpAnimation:"
+                    + " willAdd=" + add
+                    + " isHeadsUp=" + isHeadsUp
+                    + " row=" + row.getEntry().getKey());
+        }
+        if (add) {
+            // If we're hiding a HUN we just started showing THIS FRAME, then remove that event,
+            // and do not add the disappear event either.
+            if (!isHeadsUp && mHeadsUpChangeAnimations.remove(new Pair<>(row, true))) {
+                if (SPEW) {
+                    Log.v(TAG, "generateHeadsUpAnimation: previous hun appear animation cancelled");
+                }
+                return;
+            }
             mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
             mNeedsAnimation = true;
             if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ff75eef..7c3399d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -127,6 +127,7 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.Compile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -144,7 +145,7 @@
 @StatusBarComponent.StatusBarScope
 public class NotificationStackScrollLayoutController {
     private static final String TAG = "StackScrollerController";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
 
     private final boolean mAllowLongPress;
     private final NotificationGutsManager mNotificationGutsManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 9787a944..6632c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Compile;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -54,8 +55,8 @@
 
     private static final long ALERT_TRANSFER_TIMEOUT = 300;
     private static final String TAG = "NotifGroupAlertTransfer";
-    private static final boolean DEBUG = StatusBar.DEBUG;
-    private static final boolean SPEW = StatusBar.SPEW;
+    private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * The list of entries containing group alert metadata for each group. Keyed by group key.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4d625cf..23ea45b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -69,6 +69,7 @@
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserManager;
 import android.os.VibrationEffect;
 import android.provider.Settings;
@@ -4590,11 +4591,13 @@
 
         @Override
         public void onSmallestScreenWidthChanged() {
+            Trace.beginSection("onSmallestScreenWidthChanged");
             if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
 
             // Can affect multi-user switcher visibility as it depends on screen size by default:
             // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
             reInflateViews();
+            Trace.endSection();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 981c843..649aa42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1003,7 +1003,7 @@
         internalFilter.addAction(BANNER_ACTION_CANCEL);
         internalFilter.addAction(BANNER_ACTION_SETUP);
         mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
-                null);
+                null, Context.RECEIVER_EXPORTED_UNAUDITED);
 
         if (mWallpaperSupported) {
             IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
@@ -1332,7 +1332,8 @@
             demoFilter.addAction(ACTION_FAKE_ARTWORK);
         }
         mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
-                android.Manifest.permission.DUMP, null);
+                android.Manifest.permission.DUMP, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // listen for USER_SETUP_COMPLETE setting (per-user)
         mDeviceProvisionedController.addCallback(mUserSetupObserver);
@@ -2963,7 +2964,7 @@
         mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
             mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
-        } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
+        } else if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
         updatePanelExpansionForKeyguard();
@@ -3569,7 +3570,6 @@
             // once we fully woke up.
             updateRevealEffect(true /* wakingUp */);
             updateNotificationPanelTouchState();
-            mPulseExpansionHandler.onStartedWakingUp();
 
             // If we are waking up during the screen off animation, we should undo making the
             // expanded visible (we did that so the LightRevealScrim would be visible).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0ba7134..ea0dd72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -99,6 +99,7 @@
 
             override fun onAnimationEnd(animation: Animator?) {
                 lightRevealAnimationPlaying = false
+                interactionJankMonitor.end(CUJ_SCREEN_OFF)
             }
 
             override fun onAnimationStart(animation: Animator?) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 41cacf5..1030bfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -16,110 +16,53 @@
 
 package com.android.systemui.statusbar.policy;
 
-
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
-import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
-
-import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
 
 import android.annotation.Nullable;
 import android.hardware.devicestate.DeviceStateManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseIntArray;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
 
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 /**
- * Handles reading and writing of rotation lock settings per device state, as well as setting
- * the rotation lock when device state changes.
- **/
+ * Handles reading and writing of rotation lock settings per device state, as well as setting the
+ * rotation lock when device state changes.
+ */
 @SysUISingleton
-public final class DeviceStateRotationLockSettingController implements Listenable,
-        RotationLockController.RotationLockControllerCallback {
+public final class DeviceStateRotationLockSettingController
+        implements Listenable, RotationLockController.RotationLockControllerCallback {
 
     private static final String TAG = "DSRotateLockSettingCon";
 
-    private static final String SEPARATOR_REGEX = ":";
-
-    private final SecureSettings mSecureSettings;
     private final RotationPolicyWrapper mRotationPolicyWrapper;
     private final DeviceStateManager mDeviceStateManager;
     private final Executor mMainExecutor;
-    private final String[] mDeviceStateRotationLockDefaults;
+    private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
 
-    private SparseIntArray mDeviceStateRotationLockSettings;
-    // TODO(b/183001527): Add API to query current device state and initialize this.
+    // On registration for DeviceStateCallback, we will receive a callback with the current state
+    // and this will be initialized.
     private int mDeviceState = -1;
-    @Nullable
-    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
-
+    @Nullable private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+    private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+            mDeviceStateRotationLockSettingsListener;
 
     @Inject
     public DeviceStateRotationLockSettingController(
-            SecureSettings secureSettings,
             RotationPolicyWrapper rotationPolicyWrapper,
             DeviceStateManager deviceStateManager,
             @Main Executor executor,
-            @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
-    ) {
-        mSecureSettings = secureSettings;
+            DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager) {
         mRotationPolicyWrapper = rotationPolicyWrapper;
         mDeviceStateManager = deviceStateManager;
         mMainExecutor = executor;
-        mDeviceStateRotationLockDefaults = deviceStateRotationLockDefaults;
-    }
-
-    /**
-     * Loads the settings from storage.
-     */
-    public void initialize() {
-        String serializedSetting =
-                mSecureSettings.getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                        UserHandle.USER_CURRENT);
-        if (TextUtils.isEmpty(serializedSetting)) {
-            // No settings saved, we should load the defaults and persist them.
-            fallbackOnDefaults();
-            return;
-        }
-        String[] values = serializedSetting.split(SEPARATOR_REGEX);
-        if (values.length % 2 != 0) {
-            // Each entry should be a key/value pair, so this is corrupt.
-            Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
-            fallbackOnDefaults();
-            return;
-        }
-        mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
-        int key;
-        int value;
-
-        for (int i = 0; i < values.length - 1; ) {
-            try {
-                key = Integer.parseInt(values[i++]);
-                value = Integer.parseInt(values[i++]);
-                mDeviceStateRotationLockSettings.put(key, value);
-            } catch (NumberFormatException e) {
-                Log.wtf(TAG, "Error deserializing one of the saved settings", e);
-                fallbackOnDefaults();
-                return;
-            }
-        }
-    }
-
-    private void fallbackOnDefaults() {
-        loadDefaults();
-        persistSettings();
+        mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
     }
 
     @Override
@@ -129,10 +72,17 @@
             // is no user action.
             mDeviceStateCallback = this::updateDeviceState;
             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
+            mDeviceStateRotationLockSettingsListener = () -> readPersistedSetting(mDeviceState);
+            mDeviceStateRotationLockSettingsManager.registerListener(
+                    mDeviceStateRotationLockSettingsListener);
         } else {
             if (mDeviceStateCallback != null) {
                 mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
             }
+            if (mDeviceStateRotationLockSettingsListener != null) {
+                mDeviceStateRotationLockSettingsManager.unregisterListener(
+                        mDeviceStateRotationLockSettingsListener);
+            }
         }
     }
 
@@ -143,7 +93,8 @@
             return;
         }
 
-        if (rotationLocked == isRotationLockedForCurrentState()) {
+        if (rotationLocked
+                == mDeviceStateRotationLockSettingsManager.isRotationLocked(mDeviceState)) {
             Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
             return;
         }
@@ -152,19 +103,15 @@
     }
 
     private void saveNewRotationLockSetting(boolean isRotationLocked) {
-        Log.v(TAG, "saveNewRotationLockSetting [state=" + mDeviceState + "] [isRotationLocked="
-                + isRotationLocked + "]");
+        Log.v(
+                TAG,
+                "saveNewRotationLockSetting [state="
+                        + mDeviceState
+                        + "] [isRotationLocked="
+                        + isRotationLocked
+                        + "]");
 
-        mDeviceStateRotationLockSettings.put(mDeviceState,
-                isRotationLocked
-                        ? DEVICE_STATE_ROTATION_LOCK_LOCKED
-                        : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
-        persistSettings();
-    }
-
-    private boolean isRotationLockedForCurrentState() {
-        return mDeviceStateRotationLockSettings.get(mDeviceState,
-                DEVICE_STATE_ROTATION_LOCK_IGNORED) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+        mDeviceStateRotationLockSettingsManager.updateSetting(mDeviceState, isRotationLocked);
     }
 
     private void updateDeviceState(int state) {
@@ -173,8 +120,12 @@
             return;
         }
 
+        readPersistedSetting(state);
+    }
+
+    private void readPersistedSetting(int state) {
         int rotationLockSetting =
-                mDeviceStateRotationLockSettings.get(state, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+                mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
         if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
             // We won't handle this device state. The same rotation lock setting as before should
             // apply and any changes to the rotation lock setting will be written for the previous
@@ -186,54 +137,10 @@
         // Accept the new state
         mDeviceState = state;
 
-        // Update the rotation lock setting if needed for this new device state
+        // Update the rotation policy, if needed, for this new device state
         boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
         if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
             mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
         }
     }
-
-    private void persistSettings() {
-        if (mDeviceStateRotationLockSettings.size() == 0) {
-            mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                    /* value= */"", UserHandle.USER_CURRENT);
-            return;
-        }
-
-        StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append(mDeviceStateRotationLockSettings.keyAt(0))
-                .append(SEPARATOR_REGEX)
-                .append(mDeviceStateRotationLockSettings.valueAt(0));
-
-        for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
-            stringBuilder
-                    .append(SEPARATOR_REGEX)
-                    .append(mDeviceStateRotationLockSettings.keyAt(i))
-                    .append(SEPARATOR_REGEX)
-                    .append(mDeviceStateRotationLockSettings.valueAt(i));
-        }
-        mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                stringBuilder.toString(), UserHandle.USER_CURRENT);
-    }
-
-    private void loadDefaults() {
-        if (mDeviceStateRotationLockDefaults.length == 0) {
-            Log.w(TAG, "Empty default settings");
-            mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */0);
-            return;
-        }
-        mDeviceStateRotationLockSettings =
-                new SparseIntArray(mDeviceStateRotationLockDefaults.length);
-        for (String serializedDefault : mDeviceStateRotationLockDefaults) {
-            String[] entry = serializedDefault.split(SEPARATOR_REGEX);
-            try {
-                int key = Integer.parseInt(entry[0]);
-                int value = Integer.parseInt(entry[1]);
-                mDeviceStateRotationLockSettings.put(key, value);
-            } catch (NumberFormatException e) {
-                Log.wtf(TAG, "Error deserializing default settings", e);
-            }
-        }
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
new file mode 100644
index 0000000..a418c74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2022 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.policy;
+
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Manages device-state based rotation lock settings. Handles reading, writing, and listening for
+ * changes.
+ */
+public final class DeviceStateRotationLockSettingsManager {
+
+    private static final String TAG = "DSRotLockSettingsMngr";
+    private static final String SEPARATOR_REGEX = ":";
+
+    private static DeviceStateRotationLockSettingsManager sSingleton;
+
+    private final ContentResolver mContentResolver;
+    private final String[] mDeviceStateRotationLockDefaults;
+    private final Handler mMainHandler = Handler.getMain();
+    private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+    private SparseIntArray mDeviceStateRotationLockSettings;
+
+    private DeviceStateRotationLockSettingsManager(Context context) {
+        mContentResolver = context.getContentResolver();
+        mDeviceStateRotationLockDefaults =
+                context.getResources()
+                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+        initializeInMemoryMap();
+        listenForSettingsChange(context);
+    }
+
+    /** Returns a singleton instance of this class */
+    public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
+        if (sSingleton == null) {
+            sSingleton =
+                    new DeviceStateRotationLockSettingsManager(context.getApplicationContext());
+        }
+        return sSingleton;
+    }
+
+    /** Returns true if device-state based rotation lock settings are enabled. */
+    public static boolean isDeviceStateRotationLockEnabled(Context context) {
+        return context.getResources()
+                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+                        .length
+                > 0;
+    }
+
+    private void listenForSettingsChange(Context context) {
+        context.getContentResolver()
+                .registerContentObserver(
+                        Settings.Secure.getUriFor(Settings.Secure.DEVICE_STATE_ROTATION_LOCK),
+                        /* notifyForDescendents= */ false, //NOTYPO
+                        new ContentObserver(mMainHandler) {
+                            @Override
+                            public void onChange(boolean selfChange) {
+                                onPersistedSettingsChanged();
+                            }
+                        },
+                        UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+     * change. Can be called multiple times with different listeners.
+     */
+    public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+        mListeners.add(runnable);
+    }
+
+    /**
+     * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+     * was never registered.
+     */
+    public void unregisterListener(
+            DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
+        if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+            Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
+        }
+    }
+
+    /** Updates the rotation lock setting for a specified device state. */
+    public void updateSetting(int deviceState, boolean rotationLocked) {
+        mDeviceStateRotationLockSettings.put(
+                deviceState,
+                rotationLocked
+                        ? DEVICE_STATE_ROTATION_LOCK_LOCKED
+                        : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        persistSettings();
+    }
+
+    /**
+     * Returns the {@link DeviceStateRotationLockSetting} for the given device state. If no setting
+     * is specified for this device state, it will return {@link
+     * DEVICE_STATE_ROTATION_LOCK_IGNORED}.
+     */
+    @Settings.Secure.DeviceStateRotationLockSetting
+    public int getRotationLockSetting(int deviceState) {
+        return mDeviceStateRotationLockSettings.get(
+                deviceState, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+    }
+
+    /** Returns true if the rotation is locked for the current device state */
+    public boolean isRotationLocked(int deviceState) {
+        return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+    }
+
+    /**
+     * Returns true if there is no device state for which the current setting is {@link
+     * DEVICE_STATE_ROTATION_LOCK_UNLOCKED}.
+     */
+    public boolean isRotationLockedForAllStates() {
+        for (int i = 0; i < mDeviceStateRotationLockSettings.size(); i++) {
+            if (mDeviceStateRotationLockSettings.valueAt(i)
+                    == DEVICE_STATE_ROTATION_LOCK_UNLOCKED) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void initializeInMemoryMap() {
+        String serializedSetting =
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT);
+        if (TextUtils.isEmpty(serializedSetting)) {
+            // No settings saved, we should load the defaults and persist them.
+            fallbackOnDefaults();
+            return;
+        }
+        String[] values = serializedSetting.split(SEPARATOR_REGEX);
+        if (values.length % 2 != 0) {
+            // Each entry should be a key/value pair, so this is corrupt.
+            Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
+            fallbackOnDefaults();
+            return;
+        }
+        mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
+        int key;
+        int value;
+
+        for (int i = 0; i < values.length - 1; ) {
+            try {
+                key = Integer.parseInt(values[i++]);
+                value = Integer.parseInt(values[i++]);
+                mDeviceStateRotationLockSettings.put(key, value);
+            } catch (NumberFormatException e) {
+                Log.wtf(TAG, "Error deserializing one of the saved settings", e);
+                fallbackOnDefaults();
+                return;
+            }
+        }
+    }
+
+    private void fallbackOnDefaults() {
+        loadDefaults();
+        persistSettings();
+    }
+
+    private void persistSettings() {
+        if (mDeviceStateRotationLockSettings.size() == 0) {
+            Settings.Secure.putStringForUser(
+                    mContentResolver,
+                    Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                    /* value= */ "",
+                    UserHandle.USER_CURRENT);
+            return;
+        }
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder
+                .append(mDeviceStateRotationLockSettings.keyAt(0))
+                .append(SEPARATOR_REGEX)
+                .append(mDeviceStateRotationLockSettings.valueAt(0));
+
+        for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
+            stringBuilder
+                    .append(SEPARATOR_REGEX)
+                    .append(mDeviceStateRotationLockSettings.keyAt(i))
+                    .append(SEPARATOR_REGEX)
+                    .append(mDeviceStateRotationLockSettings.valueAt(i));
+        }
+        Settings.Secure.putStringForUser(
+                mContentResolver,
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                stringBuilder.toString(),
+                UserHandle.USER_CURRENT);
+    }
+
+    private void loadDefaults() {
+        if (mDeviceStateRotationLockDefaults.length == 0) {
+            Log.w(TAG, "Empty default settings");
+            mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */ 0);
+            return;
+        }
+        mDeviceStateRotationLockSettings =
+                new SparseIntArray(mDeviceStateRotationLockDefaults.length);
+        for (String serializedDefault : mDeviceStateRotationLockDefaults) {
+            String[] entry = serializedDefault.split(SEPARATOR_REGEX);
+            try {
+                int key = Integer.parseInt(entry[0]);
+                int value = Integer.parseInt(entry[1]);
+                mDeviceStateRotationLockSettings.put(key, value);
+            } catch (NumberFormatException e) {
+                Log.wtf(TAG, "Error deserializing default settings", e);
+            }
+        }
+    }
+
+    /**
+     * Called when the persisted settings have changed, requiring a reinitialization of the
+     * in-memory map.
+     */
+    @VisibleForTesting
+    public void onPersistedSettingsChanged() {
+        initializeInMemoryMap();
+        notifyListeners();
+    }
+
+    private void notifyListeners() {
+        for (DeviceStateRotationLockSettingsListener r : mListeners) {
+            r.onSettingsChanged();
+        }
+    }
+
+    /** Listener for changes in device-state based rotation lock settings */
+    public interface DeviceStateRotationLockSettingsListener {
+        /** Called whenever the settings have changed. */
+        void onSettingsChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index c1f63b8..05a586b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -128,7 +128,7 @@
                         update(true /* updateAlways */);
                     }
                 }
-            }, filter, null, null);
+            }, filter, null, null, Context.RECEIVER_EXPORTED_UNAUDITED);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 3143a47..1eeb0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -64,7 +64,6 @@
         mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController;
         mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0;
         if (mIsPerDeviceStateRotationLockEnabled) {
-            deviceStateRotationLockSettingController.initialize();
             mCallbacks.add(mDeviceStateRotationLockSettingController);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
index f42e388..29285f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -74,7 +74,7 @@
         profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
         profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
-                null, null);
+                null, null, Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 79ee746..9f20bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -229,7 +229,8 @@
 
         filter = new IntentFilter();
         mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
-                PERMISSION_SELF, null /* scheduler */);
+                PERMISSION_SELF, null /* scheduler */,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         mSettingsObserver = new ContentObserver(mHandler) {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index b6a96a7..60938fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy.dagger;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.os.UserManager;
 
@@ -35,6 +36,7 @@
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.DevicePostureControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
@@ -163,6 +165,14 @@
         return controller;
     }
 
+    /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */
+    @SysUISingleton
+    @Provides
+    static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager(
+            Context context) {
+        return DeviceStateRotationLockSettingsManager.getInstance(context);
+    }
+
     /**
      * Default values for per-device state rotation lock settings.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index bd84520..31c7006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -47,6 +47,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener;
 
 import java.util.Optional;
 
@@ -81,7 +83,8 @@
             WindowManager windowManager,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
-            @Main Resources resources) {
+            @Main Resources resources,
+            Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
         mContext = context;
         mWindowManager = windowManager;
         mIWindowManager = iWindowManager;
@@ -94,6 +97,10 @@
         if (mBarHeight < 0) {
             mBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         }
+        unfoldTransitionProgressProvider.ifPresent(
+                unfoldProgressProvider -> unfoldProgressProvider.addCallback(
+                        new JankMonitorTransitionProgressListener(
+                                /* attachedViewProvider=*/ () -> mStatusBarWindowView)));
     }
 
     public int getStatusBarHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index c2fd34c..178d014 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -17,87 +17,38 @@
 package com.android.systemui.unfold
 
 import android.content.Context
-import android.hardware.SensorManager
-import android.hardware.devicestate.DeviceStateManager
-import android.os.Handler
 import android.view.IWindowManager
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.LifecycleScreenStatusProvider
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
 import com.android.systemui.util.time.SystemClockImpl
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
-import java.util.concurrent.Executor
 import javax.inject.Named
 import javax.inject.Singleton
 
-@Module
+@Module(includes = [UnfoldSharedModule::class])
 class UnfoldTransitionModule {
 
-    @Provides
-    @Singleton
-    fun provideUnfoldTransitionProgressProvider(
-        context: Context,
-        config: UnfoldTransitionConfig,
-        screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
-        deviceStateManager: DeviceStateManager,
-        sensorManager: SensorManager,
-        @Main executor: Executor,
-        @Main handler: Handler
-    ): Optional<UnfoldTransitionProgressProvider> =
-        if (config.isEnabled) {
-            Optional.of(
-                createUnfoldTransitionProgressProvider(
-                    context,
-                    config,
-                    screenStatusProvider.get(),
-                    deviceStateManager,
-                    sensorManager,
-                    handler,
-                    executor,
-                    tracingTagPrefix = "systemui"))
-        } else {
-            Optional.empty()
-        }
-
-    @Provides
-    @Singleton
-    fun provideFoldStateProvider(
-        context: Context,
-        config: UnfoldTransitionConfig,
-        screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
-        deviceStateManager: DeviceStateManager,
-        sensorManager: SensorManager,
-        @Main executor: Executor,
-        @Main handler: Handler
-    ): Optional<FoldStateProvider> =
-        if (!config.isHingeAngleEnabled) {
-            Optional.empty()
-        } else {
-            Optional.of(
-                createFoldStateProvider(
-                    context,
-                    config,
-                    screenStatusProvider.get(),
-                    deviceStateManager,
-                    sensorManager,
-                    handler,
-                    executor))
-        }
+    @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
 
     @Provides
     @Singleton
     fun providesFoldStateLoggingProvider(
-        optionalFoldStateProvider: Optional<FoldStateProvider>
+        config: UnfoldTransitionConfig,
+        foldStateProvider: Lazy<FoldStateProvider>
     ): Optional<FoldStateLoggingProvider> =
-        optionalFoldStateProvider.map { foldStateProvider ->
-            FoldStateLoggingProviderImpl(foldStateProvider, SystemClockImpl())
+        if (config.isHingeAngleEnabled) {
+            Optional.of(FoldStateLoggingProviderImpl(foldStateProvider.get(), SystemClockImpl()))
+        } else {
+            Optional.empty()
         }
 
     @Provides
@@ -144,6 +95,9 @@
         } else {
             ShellUnfoldProgressProvider.NO_PROVIDER
         }
+
+    @Provides
+    fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
 }
 
 const val UNFOLD_STATUS_BAR = "unfold_status_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 90c7f1f..cf361ec 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -167,9 +167,11 @@
         mStorageManager.registerListener(mListener);
 
         mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
-                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+                android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // Kick current state into place
         final List<DiskInfo> disks = mStorageManager.getDisks();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
index a40cf4f..5b188b2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
@@ -63,7 +63,8 @@
         setOnDismissListener(this);
 
         final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        context.registerReceiver(mReceiver, filter);
+        context.registerReceiver(mReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
     }
 
     abstract protected void cleanUp();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
index 7d55623..7c121e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -79,7 +80,7 @@
         intent.putExtra(SliceBroadcastRelay.EXTRA_URI, testUri);
 
         mRelayHandler.handleIntent(intent);
-        verify(mSpyContext).registerReceiver(any(), eq(value));
+        verify(mSpyContext).registerReceiver(any(), eq(value), anyInt());
     }
 
     @Test
@@ -99,7 +100,7 @@
 
         mRelayHandler.handleIntent(intent);
         ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
+        verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
 
         intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
         intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
@@ -138,7 +139,7 @@
 
         mRelayHandler.handleIntent(intent);
         ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
+        verify(mSpyContext).registerReceiver(relay.capture(), eq(value), anyInt());
         relay.getValue().onReceive(mSpyContext, new Intent(TEST_ACTION));
 
         verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index 3d679de..0674ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -83,6 +83,16 @@
     }
 
     @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+        if (receiver != null) {
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
+        }
+        return super.registerReceiver(receiver, filter, flags);
+    }
+
+    @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
             String broadcastPermission, Handler scheduler) {
         if (receiver != null) {
@@ -94,6 +104,17 @@
     }
 
     @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler, int flags) {
+        if (receiver != null) {
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
+        }
+        return super.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags);
+    }
+
+    @Override
     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
             IntentFilter filter, String broadcastPermission, Handler scheduler) {
         if (receiver != null) {
@@ -105,6 +126,18 @@
     }
 
     @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+        if (receiver != null) {
+            synchronized (mRegisteredReceivers) {
+                mRegisteredReceivers.add(receiver);
+            }
+        }
+        return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler,
+                flags);
+    }
+
+    @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
         if (receiver != null) {
             synchronized (mRegisteredReceivers) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index cf53ccf..7c5f57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -19,10 +19,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
+import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,6 +48,8 @@
 @RunWith(AndroidTestingRunner.class)
 public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
     private static final int DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT = 100;
+    private static final int MAX_BURN_IN_OFFSET = 20;
+    private static final long BURN_IN_PROTECTION_UPDATE_INTERVAL = 10;
 
     @Mock
     Resources mResources;
@@ -61,6 +66,9 @@
     @Mock
     ViewGroup mDreamOverlayContentView;
 
+    @Mock
+    Handler mHandler;
+
     DreamOverlayContainerViewController mController;
 
     @Before
@@ -74,8 +82,12 @@
         when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
 
         mController = new DreamOverlayContainerViewController(
-                mDreamOverlayContainerView, mDreamOverlayContentView,
-                mDreamOverlayStatusBarViewController);
+                mDreamOverlayContainerView,
+                mDreamOverlayContentView,
+                mDreamOverlayStatusBarViewController,
+                mHandler,
+                MAX_BURN_IN_OFFSET,
+                BURN_IN_PROTECTION_UPDATE_INTERVAL);
     }
 
     @Test
@@ -129,4 +141,37 @@
         computeInsetsListenerCapture.getValue().onComputeInternalInsets(info);
         assertNotNull(info.touchableRegion);
     }
+
+    @Test
+    public void testBurnInProtectionStartsWhenContentViewAttached() {
+        mController.onViewAttached();
+        verify(mHandler).postDelayed(any(Runnable.class), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
+    }
+
+    @Test
+    public void testBurnInProtectionStopsWhenContentViewDetached() {
+        mController.onViewDetached();
+        verify(mHandler).removeCallbacks(any(Runnable.class));
+    }
+
+    @Test
+    public void testBurnInProtectionUpdatesPeriodically() {
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mController.onViewAttached();
+        verify(mHandler).postDelayed(
+                runnableCaptor.capture(), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
+        runnableCaptor.getValue().run();
+        verify(mDreamOverlayContainerView).setTranslationX(anyFloat());
+        verify(mDreamOverlayContainerView).setTranslationY(anyFloat());
+    }
+
+    @Test
+    public void testBurnInProtectionReschedulesUpdate() {
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mController.onViewAttached();
+        verify(mHandler).postDelayed(
+                runnableCaptor.capture(), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
+        runnableCaptor.getValue().run();
+        verify(mHandler).postDelayed(runnableCaptor.getValue(), BURN_IN_PROTECTION_UPDATE_INTERVAL);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index cb16bec..87bc732 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -76,7 +76,8 @@
         )
         verify(mFlagManager).restartAction = any()
         mBroadcastReceiver = withArgCaptor {
-            verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable())
+            verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(),
+                any())
         }
         mClearCacheAction = withArgCaptor {
             verify(mFlagManager).clearCacheAction = capture()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index e453ff2d..fd282cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -80,6 +80,7 @@
         assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness());
 
         verify(mWakefulnessObserver).onFinishedWakingUp();
+        verify(mWakefulnessObserver).onPostFinishedWakingUp();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index dec5a10..4839bde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -16,23 +16,28 @@
 
 package com.android.systemui.media.taptotransfer
 
+import android.content.ComponentName
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer
-import com.android.systemui.media.taptotransfer.sender.TransferInitiated
-import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.media.taptotransfer.sender.*
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
+import org.mockito.Mockito.anyString
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -51,10 +56,19 @@
     private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender
     @Mock
     private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
+    @Mock
+    private lateinit var mediaSenderService: IDeviceSenderCallback.Stub
+    private lateinit var mediaSenderServiceComponentName: ComponentName
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+
+        mediaSenderServiceComponentName = ComponentName(context, MediaTttSenderService::class.java)
+        context.addMockService(mediaSenderServiceComponentName, mediaSenderService)
+        whenever(mediaSenderService.queryLocalInterface(anyString())).thenReturn(mediaSenderService)
+        whenever(mediaSenderService.asBinder()).thenReturn(mediaSenderService)
+
         mediaTttCommandLineHelper =
             MediaTttCommandLineHelper(
                 commandRegistry,
@@ -102,10 +116,14 @@
     }
 
     @Test
-    fun sender_moveCloserToTransfer_chipDisplayWithCorrectState() {
-        commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+    fun sender_moveCloserToStartCast_serviceCallbackCalled() {
+        commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand())
 
-        verify(mediaTttChipControllerSender).displayChip(any(MoveCloserToTransfer::class.java))
+        assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+
+        val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+        verify(mediaSenderService).closeToReceiverToStartCast(any(), capture(deviceInfoCaptor))
+        assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
     }
 
     @Test
@@ -143,11 +161,11 @@
         verify(mediaTttChipControllerReceiver).removeChip()
     }
 
-    private fun getMoveCloserToTransferCommand(): Array<String> =
+    private fun getMoveCloserToStartCastCommand(): Array<String> =
         arrayOf(
             ADD_CHIP_COMMAND_SENDER_TAG,
             DEVICE_NAME,
-            MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME
+            MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
         )
 
     private fun getTransferInitiatedCommand(): Array<String> =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index caef5b9..ecc4c46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -66,8 +66,8 @@
     }
 
     @Test
-    fun moveCloserToTransfer_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
-        controllerSender.displayChip(moveCloserToTransfer())
+    fun moveCloserToStartCast_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
+        controllerSender.displayChip(moveCloserToStartCast())
 
         val chipView = getChipView()
         assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
@@ -192,8 +192,8 @@
     }
 
     @Test
-    fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
-        controllerSender.displayChip(moveCloserToTransfer())
+    fun changeFromCloserToStartToTransferInitiated_loadingIconAppears() {
+        controllerSender.displayChip(moveCloserToStartCast())
         controllerSender.displayChip(transferInitiated())
 
         assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
@@ -216,9 +216,9 @@
     }
 
     @Test
-    fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
+    fun changeFromTransferSucceededToMoveCloserToStart_undoButtonDisappears() {
         controllerSender.displayChip(transferSucceeded())
-        controllerSender.displayChip(moveCloserToTransfer())
+        controllerSender.displayChip(moveCloserToStartCast())
 
         assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
     }
@@ -240,8 +240,8 @@
     }
 
     /** Helper method providing default parameters to not clutter up the tests. */
-    private fun moveCloserToTransfer() =
-        MoveCloserToTransfer(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+    private fun moveCloserToStartCast() =
+        MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferInitiated(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
new file mode 100644
index 0000000..8f64698
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -0,0 +1,48 @@
+package com.android.systemui.media.taptotransfer.sender
+
+import android.media.MediaRoute2Info
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.mediattt.DeviceInfo
+import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MediaTttSenderServiceTest : SysuiTestCase() {
+
+    private lateinit var service: MediaTttSenderService
+    private lateinit var callback: IDeviceSenderCallback
+
+    @Mock
+    private lateinit var controller: MediaTttChipControllerSender
+
+    private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
+        .addFeature("feature")
+        .build()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        service = MediaTttSenderService(context, controller)
+        callback = IDeviceSenderCallback.Stub.asInterface(service.onBind(null))
+    }
+
+    @Test
+    fun closeToReceiverToStartCast_controllerTriggeredWithMoveCloserToStartCastState() {
+        val name = "Fake name"
+        callback.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name))
+
+        val chipStateCaptor = argumentCaptor<MoveCloserToStartCast>()
+        verify(controller).displayChip(capture(chipStateCaptor))
+
+        val chipState = chipStateCaptor.value!!
+        assertThat(chipState.otherDeviceName).isEqualTo(name)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 13b8e81..42647f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -4,13 +4,12 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
 import com.android.systemui.ExpandHelper
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.media.MediaHierarchyManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
@@ -64,12 +63,11 @@
     @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
     @Mock lateinit var falsingCollector: FalsingCollector
     @Mock lateinit var ambientState: AmbientState
-    @Mock lateinit var displayMetrics: DisplayMetrics
+    @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
     @Mock lateinit var scrimController: ScrimController
     @Mock lateinit var configurationController: ConfigurationController
     @Mock lateinit var falsingManager: FalsingManager
-    @Mock lateinit var buffer: LogBuffer
     @Mock lateinit var notificationPanelController: NotificationPanelViewController
     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
     @Mock lateinit var depthController: NotificationShadeDepthController
@@ -98,6 +96,7 @@
             mediaHierarchyManager = mediaHierarchyManager,
             scrimController = scrimController,
             depthController = depthController,
+            wakefulnessLifecycle = wakefulnessLifecycle,
             context = context,
             configurationController = configurationController,
             falsingManager = falsingManager,
@@ -148,6 +147,23 @@
     }
 
     @Test
+    fun testWakingToShadeLockedWhenDozing() {
+        whenever(statusbarStateController.isDozing).thenReturn(true)
+        transitionController.goToLockedShade(null)
+        verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+        assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
+    }
+
+    @Test
+    fun testNotWakingToShadeLockedWhenNotDozing() {
+        whenever(statusbarStateController.isDozing).thenReturn(false)
+        transitionController.goToLockedShade(null)
+        verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+        assertFalse("Waking to shade locked when not dozing",
+                transitionController.isWakingToShadeLocked)
+    }
+
+    @Test
     fun testGoToLockedShadeOnlyOnKeyguard() {
         whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         transitionController.goToLockedShade(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index b31dd3c..eef9dd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -23,7 +23,7 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isA;
@@ -209,8 +209,6 @@
         when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
         doAnswer(invocation -> {
             mUserCallback = (DeviceProvisionedListener) invocation.getArguments()[0];
-            mUserCallback.onUserSetupChanged();
-            mUserCallback.onDeviceProvisionedChanged();
             TestableLooper.get(this).processAllMessages();
             return null;
         }).when(mMockProvisionController).addCallback(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 73eddd1..6262a9b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -35,6 +35,7 @@
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.settingslib.graph.SignalDrawable;
@@ -60,6 +61,72 @@
 public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
 
     @Test
+    public void testDeviceProvisioned_userNotSetUp() {
+        // GIVEN - user is not setup
+        when(mMockProvisionController.isCurrentUserSetup()).thenReturn(false);
+
+        // WHEN - a NetworkController is created
+        mNetworkController = new NetworkControllerImpl(mContext,
+                mMockCm,
+                mMockTm,
+                mTelephonyListenerManager,
+                mMockWm,
+                mMockNsm,
+                mMockSm,
+                mConfig,
+                TestableLooper.get(this).getLooper(),
+                mFakeExecutor,
+                mCallbackHandler,
+                mock(AccessPointControllerImpl.class),
+                mock(DataUsageController.class),
+                mMockSubDefaults,
+                mMockProvisionController,
+                mMockBd,
+                mDemoModeController,
+                mCarrierConfigTracker,
+                mFeatureFlags,
+                mock(DumpManager.class)
+        );
+        TestableLooper.get(this).processAllMessages();
+
+        // THEN - NetworkController claims the user is not setup
+        assertFalse("User has not been set up", mNetworkController.isUserSetup());
+    }
+
+    @Test
+    public void testDeviceProvisioned_userSetUp() {
+        // GIVEN - user is not setup
+        when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
+
+        // WHEN - a NetworkController is created
+        mNetworkController = new NetworkControllerImpl(mContext,
+                mMockCm,
+                mMockTm,
+                mTelephonyListenerManager,
+                mMockWm,
+                mMockNsm,
+                mMockSm,
+                mConfig,
+                TestableLooper.get(this).getLooper(),
+                mFakeExecutor,
+                mCallbackHandler,
+                mock(AccessPointControllerImpl.class),
+                mock(DataUsageController.class),
+                mMockSubDefaults,
+                mMockProvisionController,
+                mMockBd,
+                mDemoModeController,
+                mCarrierConfigTracker,
+                mFeatureFlags,
+                mock(DumpManager.class)
+        );
+        TestableLooper.get(this).processAllMessages();
+
+        // THEN - NetworkController claims the user is not setup
+        assertTrue("User has been set up", mNetworkController.isUserSetup());
+    }
+
+    @Test
     public void testNoIconWithoutMobile() {
         // Turn off mobile network support.
         when(mMockTm.isDataCapable()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index a69ac58..f08180d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1469,6 +1469,18 @@
     }
 
     @Test
+    public void testCannotDismissNoClearNotifications() {
+        // GIVEN an no-clear notification
+        final NotificationEntry container = new NotificationEntryBuilder()
+                .setFlag(mContext, FLAG_NO_CLEAR, true)
+                .build();
+
+        // THEN its children are not dismissible
+        assertFalse(mCollection.shouldAutoDismissChildren(
+                container, container.getSbn().getGroupKey()));
+    }
+
+    @Test
     public void testCanDismissFgsNotificationChildren() {
         // GIVEN an FGS but not ongoing notification
         final NotificationEntry container = new NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index f70330d..bde6734 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -40,6 +40,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.SectionClassifier;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
@@ -92,6 +93,7 @@
     @Mock private NotifSection mNotifSection;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private IStatusBarService mService;
+    @Mock private ConversationNotificationManager mConvoManager;
     @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
     private final SectionClassifier mSectionClassifier = new SectionClassifier();
     private final NotifUiAdjustmentProvider mAdjustmentProvider =
@@ -119,6 +121,7 @@
                 mock(NotifViewBarn.class),
                 mAdjustmentProvider,
                 mService,
+                mConvoManager,
                 TEST_CHILD_BIND_CUTOFF,
                 TEST_MAX_GROUP_DELAY);
 
@@ -405,6 +408,13 @@
     }
 
     @Test
+    public void testCallConversationManagerBindWhenInflated() {
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+        mNotifInflater.getInflateCallback(mEntry).onInflationFinished(mEntry, null);
+        verify(mConvoManager, times(1)).onEntryViewBound(eq(mEntry));
+    }
+
+    @Test
     public void testPartiallyInflatedGroupsAreReleasedAfterTimeout() {
         // GIVEN a newly-posted group with a summary and two children
         final GroupEntry group = new GroupEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 1be27da..6d170b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -178,20 +178,68 @@
     }
 
     /**
+     * Helper for testing various sibling counts
+     */
+    private void helpTestAlertOverrideWithSiblings(int numSiblings) {
+        helpTestAlertOverride(
+                /* numSiblings */ numSiblings,
+                /* summaryAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* expectAlertOverride */ true);
+    }
+
+    @Test
+    public void testAlertOverrideWithParentAlertAll() {
+        // tests that summary can have GROUP_ALERT_ALL and this still works
+        helpTestAlertOverride(
+                /* numSiblings */ 1,
+                /* summaryAlert */ Notification.GROUP_ALERT_ALL,
+                /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* expectAlertOverride */ true);
+    }
+
+    @Test
+    public void testAlertOverrideWithParentAlertChild() {
+        // Tests that if the summary alerts CHILDREN, there's no alertOverride
+        helpTestAlertOverride(
+                /* numSiblings */ 1,
+                /* summaryAlert */ Notification.GROUP_ALERT_CHILDREN,
+                /* childAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* siblingAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* expectAlertOverride */ false);
+    }
+
+    @Test
+    public void testAlertOverrideWithChildrenAlertAll() {
+        // Tests that if the children alert ALL, there's no alertOverride
+        helpTestAlertOverride(
+                /* numSiblings */ 1,
+                /* summaryAlert */ Notification.GROUP_ALERT_SUMMARY,
+                /* childAlert */ Notification.GROUP_ALERT_ALL,
+                /* siblingAlert */ Notification.GROUP_ALERT_ALL,
+                /* expectAlertOverride */ false);
+    }
+
+    /**
      * This tests, for a group with a priority entry and the given number of siblings, that:
      * 1) the priority entry is identified as the alertOverride for the group
      * 2) the onAlertOverrideChanged method is called at that time
      * 3) when the priority entry is removed, these are reversed
      */
-    private void helpTestAlertOverrideWithSiblings(int numSiblings) {
-        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+    private void helpTestAlertOverride(int numSiblings,
+            @Notification.GroupAlertBehavior int summaryAlert,
+            @Notification.GroupAlertBehavior int childAlert,
+            @Notification.GroupAlertBehavior int siblingAlert,
+            boolean expectAlertOverride) {
         // Create entries in an order so that the priority entry can be deemed the newest child.
         NotificationEntry[] siblings = new NotificationEntry[numSiblings];
         for (int i = 0; i < numSiblings; i++) {
-            siblings[i] = mGroupTestHelper.createChildNotification(groupAlert);
+            siblings[i] = mGroupTestHelper.createChildNotification(siblingAlert);
         }
-        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
-        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(childAlert);
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(summaryAlert);
 
         // The priority entry is an important conversation.
         when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
@@ -208,6 +256,14 @@
         }
         mGroupManager.onEntryAdded(priorityEntry);
 
+        if (!expectAlertOverride) {
+            // Test expectation is that there will NOT be an alert, so verify that!
+            NotificationGroup summaryGroup =
+                    mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+            assertNull(summaryGroup.alertOverride);
+            return;
+        }
+
         // Verify that the summary group has the priority child as its alertOverride
         NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
         assertEquals(priorityEntry, summaryGroup.alertOverride);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index db7b2f2..a8522c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -25,14 +29,15 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContentResolver;
 import android.testing.TestableResources;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
 import com.android.internal.view.RotationPolicy;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
 
@@ -47,63 +52,55 @@
 @SmallTest
 public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
 
-    private static final String[] DEFAULT_SETTINGS = new String[]{
-            "0:0",
-            "1:2"
-    };
+    private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
 
-    private final FakeSettings mFakeSettings = new FakeSettings();
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
     @Mock DeviceStateManager mDeviceStateManager;
     RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
     DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+    private DeviceStateRotationLockSettingsManager mSettingsManager;
+    private TestableContentResolver mContentResolver;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(/* testClass= */ this);
         TestableResources resources = mContext.getOrCreateTestableResources();
+        resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS);
 
         ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor =
-                ArgumentCaptor.forClass(
-                        DeviceStateManager.DeviceStateCallback.class);
+                ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
 
-        mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController(
-                mFakeSettings,
-                mFakeRotationPolicy,
-                mDeviceStateManager,
-                mFakeExecutor,
-                DEFAULT_SETTINGS
-        );
+        mContentResolver = mContext.getContentResolver();
+        mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext);
+        mDeviceStateRotationLockSettingController =
+                new DeviceStateRotationLockSettingController(
+                        mFakeRotationPolicy, mDeviceStateManager, mFakeExecutor, mSettingsManager);
 
         mDeviceStateRotationLockSettingController.setListening(true);
-        verify(mDeviceStateManager).registerCallback(any(),
-                deviceStateCallbackArgumentCaptor.capture());
+        verify(mDeviceStateManager)
+                .registerCallback(any(), deviceStateCallbackArgumentCaptor.capture());
         mDeviceStateCallback = deviceStateCallbackArgumentCaptor.getValue();
     }
 
     @Test
     public void whenSavedSettingsEmpty_defaultsLoadedAndSaved() {
-        mFakeSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, "",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith();
 
-        mDeviceStateRotationLockSettingController.initialize();
-
-        assertThat(mFakeSettings
-                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                        UserHandle.USER_CURRENT))
+        assertThat(
+                        Settings.Secure.getStringForUser(
+                                mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                                UserHandle.USER_CURRENT))
                 .isEqualTo("0:0:1:2");
     }
 
     @Test
     public void whenNoSavedValueForDeviceState_assumeIgnored() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:2:1:2",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -116,52 +113,43 @@
 
     @Test
     public void whenDeviceStateSwitched_loadCorrectSetting() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:2:1:1",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED);
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(0);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
-
     }
 
     @Test
     public void whenUserChangesSetting_saveSettingForCurrentState() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:1:1:2",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mSettingsManager.onPersistedSettingsChanged();
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(0);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
 
-        mDeviceStateRotationLockSettingController
-                .onRotationLockStateChanged(/* rotationLocked= */false,
-                        /* affordanceVisible= */ true);
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ false, /* affordanceVisible= */ true);
 
-        assertThat(mFakeSettings
-                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                        UserHandle.USER_CURRENT))
+        assertThat(
+                        Settings.Secure.getStringForUser(
+                                mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                                UserHandle.USER_CURRENT))
                 .isEqualTo("0:2:1:2");
     }
 
-
     @Test
     public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:0:1:2",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -172,12 +160,9 @@
 
     @Test
     public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
-        mFakeSettings.putStringForUser(
-                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                /* value= */"0:0:1:2",
-                UserHandle.USER_CURRENT);
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
-        mDeviceStateRotationLockSettingController.initialize();
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -185,16 +170,52 @@
         mDeviceStateCallback.onStateChanged(0);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
-        mDeviceStateRotationLockSettingController
-                .onRotationLockStateChanged(/* rotationLocked= */true,
-                        /* affordanceVisible= */ true);
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ true, /* affordanceVisible= */ true);
 
-        assertThat(mFakeSettings
-                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
-                        UserHandle.USER_CURRENT))
+        assertThat(
+                        Settings.Secure.getStringForUser(
+                                mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                                UserHandle.USER_CURRENT))
                 .isEqualTo("0:0:1:1");
     }
 
+    @Test
+    public void whenSettingsChangedExternally_updateRotationPolicy() throws InterruptedException {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mFakeRotationPolicy.setRotationLock(false);
+        mDeviceStateCallback.onStateChanged(0);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        // Changing device state 0 to LOCKED
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+    }
+
+    private void initializeSettingsWith(int... values) {
+        if (values.length % 2 != 0) {
+            throw new IllegalArgumentException("Expecting key-value pairs");
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < values.length; sb.append(":")) {
+            sb.append(values[i++]).append(":").append(values[i++]);
+        }
+
+        Settings.Secure.putStringForUser(
+                mContentResolver,
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                sb.toString(),
+                UserHandle.USER_CURRENT);
+
+        mSettingsManager.onPersistedSettingsChanged();
+    }
+
     private static class FakeRotationPolicy implements RotationPolicyWrapper {
 
         private boolean mRotationLock;
@@ -230,8 +251,8 @@
         }
 
         @Override
-        public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
-                int userHandle) {
+        public void registerRotationPolicyListener(
+                RotationPolicy.RotationPolicyListener listener, int userHandle) {
             throw new AssertionError("Not implemented");
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index ba7bbfe..20a3fda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,7 +102,8 @@
 
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
-                Handler.createAsync(Dependency.get(Dependency.BG_LOOPER)));
+                Handler.createAsync(Dependency.get(Dependency.BG_LOOPER)),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // Avoid SecurityException RemoteInputView#sendRemoteInput().
         mContext.addMockSystemService(ShortcutManager.class, mShortcutManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
index 0581264..ea620a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -23,7 +23,6 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.testing.TestableResources;
 
 import androidx.test.filters.SmallTest;
 
@@ -43,25 +42,19 @@
 @SmallTest
 public class RotationLockControllerImplTest extends SysuiTestCase {
 
-    private static final String[] DEFAULT_SETTINGS = new String[]{
-            "0:0",
-            "1:2"
-    };
+    private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
 
     @Mock RotationPolicyWrapper mRotationPolicyWrapper;
     @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
 
-    private TestableResources mResources;
-    private ArgumentCaptor<RotationPolicy.RotationPolicyListener>
-            mRotationPolicyListenerCaptor;
+    private ArgumentCaptor<RotationPolicy.RotationPolicyListener> mRotationPolicyListenerCaptor;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(/* testClass= */ this);
-        mResources = mContext.getOrCreateTestableResources();
 
-        mRotationPolicyListenerCaptor = ArgumentCaptor.forClass(
-                RotationPolicy.RotationPolicyListener.class);
+        mRotationPolicyListenerCaptor =
+                ArgumentCaptor.forClass(RotationPolicy.RotationPolicyListener.class);
     }
 
     @Test
@@ -79,14 +72,7 @@
     }
 
     @Test
-    public void whenFlagOn_initializesDeviceStateRotationController() {
-        createRotationLockController();
-
-        verify(mDeviceStateRotationLockSettingController).initialize();
-    }
-
-    @Test
-    public void whenFlagOn_dviceStateRotationControllerAddedToCallbacks() {
+    public void whenFlagOn_deviceStateRotationControllerAddedToCallbacks() {
         createRotationLockController();
         captureRotationPolicyListener().onChange();
 
@@ -103,11 +89,11 @@
     private void createRotationLockController() {
         createRotationLockController(DEFAULT_SETTINGS);
     }
+
     private void createRotationLockController(String[] deviceStateRotationLockDefaults) {
         new RotationLockControllerImpl(
                 mRotationPolicyWrapper,
                 mDeviceStateRotationLockSettingController,
-                deviceStateRotationLockDefaults
-        );
+                deviceStateRotationLockDefaults);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 32aee2b..0e25d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -30,6 +30,7 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
@@ -119,7 +120,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mReceiver = new BlockingQueueIntentReceiver();
-        mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
+        mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> action.onDismiss());
         mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
         mDependency.injectMockDependency(ShadeController.class);
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 6d0eadb..196c6aa 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -279,6 +279,9 @@
     // Notify the user that a CA certificate is pending for the wifi connection.
     NOTE_SERVER_CA_CERTIFICATE = 67;
 
+    // Notify the user to set up dream
+    NOTE_SETUP_DREAM = 68;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index 74d7f65..f33c8c0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -77,6 +77,7 @@
         ":services.appwidget-sources",
         ":services.autofill-sources",
         ":services.backup-sources",
+        ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex/service-bluetooth jar is ready
         ":backuplib-sources",
         ":services.companion-sources",
         ":services.contentcapture-sources",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index aba32ec..59c1461 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -667,10 +667,6 @@
                 return null;
             }
 
-            // Don't need to add the embedded hierarchy windows into the accessibility windows list.
-            if (mHostEmbeddedMap.size() > 0 && isEmbeddedHierarchyWindowsLocked(windowId)) {
-                return null;
-            }
             final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
 
             reportedWindow.setId(windowId);
@@ -703,21 +699,6 @@
             return reportedWindow;
         }
 
-        private boolean isEmbeddedHierarchyWindowsLocked(int windowId) {
-            final IBinder leashToken = mWindowIdMap.get(windowId);
-            if (leashToken == null) {
-                return false;
-            }
-
-            for (int i = 0; i < mHostEmbeddedMap.size(); i++) {
-                if (mHostEmbeddedMap.keyAt(i).equals(leashToken)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
         private int getTypeForWindowManagerWindowType(int windowType) {
             switch (windowType) {
                 case WindowManager.LayoutParams.TYPE_APPLICATION:
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 1914164..93fc0e72 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -23,7 +23,7 @@
 import static android.content.ComponentName.createRelative;
 
 import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
 import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
 import static com.android.server.companion.RolesUtils.isRoleHolder;
 
@@ -31,6 +31,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.companion.AssociationInfo;
@@ -102,8 +103,9 @@
  * @see #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback,
  * ResultReceiver, MacAddress)
  */
+@SuppressLint("LongLogTag")
 class AssociationRequestsProcessor {
-    private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor";
+    private static final String TAG = "CompanionDevice_AssociationRequestsProcessor";
 
     private static final ComponentName ASSOCIATION_REQUEST_APPROVAL_ACTIVITY =
             createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".CompanionDeviceActivity");
@@ -161,7 +163,7 @@
 
         // 1. Enforce permissions and other requirements.
         enforcePermissionsForAssociation(mContext, request, packageUid);
-        mService.checkUsesFeature(packageName, userId);
+        enforceUsesCompanionDeviceFeature(mContext, userId, packageName);
 
         // 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER
         // to perform discovery NOR to collect user consent).
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 3f0200e..dbcdd0f 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -37,6 +37,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.StringJoiner;
 
 /**
  * Implementation of the {@link AssociationStore}, with addition of the methods for modification.
@@ -58,33 +59,15 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private final Map<Integer, AssociationInfo> mIdMap;
+    private final Map<Integer, AssociationInfo> mIdMap = new HashMap<>();
     @GuardedBy("mLock")
-    private final Map<MacAddress, Set<Integer>> mAddressMap;
+    private final Map<MacAddress, Set<Integer>> mAddressMap = new HashMap<>();
     @GuardedBy("mLock")
     private final SparseArray<List<AssociationInfo>> mCachedPerUser = new SparseArray<>();
 
     @GuardedBy("mListeners")
     private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
 
-    AssociationStoreImpl(Collection<AssociationInfo> associations) {
-        synchronized (mLock) {
-            final int size = associations.size();
-            mIdMap = new HashMap<>(size);
-            mAddressMap = new HashMap<>(size);
-
-            for (AssociationInfo association : associations) {
-                final int id = association.getId();
-                mIdMap.put(id, association);
-
-                final MacAddress address = association.getDeviceMacAddress();
-                if (address != null) {
-                    mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
-                }
-            }
-        }
-    }
-
     void addAssociation(@NonNull AssociationInfo association) {
         final int id = association.getId();
 
@@ -136,6 +119,8 @@
 
             // Update the ID-to-Association map.
             mIdMap.put(id, updated);
+            // Invalidate the corresponding user cache entry.
+            invalidateCacheForUserLocked(current.getUserId());
 
             // Update the MacAddress-to-List<Association> map if needed.
             final MacAddress updatedAddress = updated.getDeviceMacAddress();
@@ -299,4 +284,38 @@
             }
         }
     }
+
+    void setAssociations(Collection<AssociationInfo> allAssociations) {
+        if (DEBUG) {
+            Log.i(TAG, "setAssociations() n=" + allAssociations.size());
+            final StringJoiner stringJoiner = new StringJoiner(", ");
+            allAssociations.forEach(assoc -> stringJoiner.add(assoc.toShortString()));
+            Log.v(TAG, "  associations=" + stringJoiner);
+        }
+        synchronized (mLock) {
+            setAssociationsLocked(allAssociations);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void setAssociationsLocked(Collection<AssociationInfo> associations) {
+        clearLocked();
+
+        for (AssociationInfo association : associations) {
+            final int id = association.getId();
+            mIdMap.put(id, association);
+
+            final MacAddress address = association.getDeviceMacAddress();
+            if (address != null) {
+                mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void clearLocked() {
+        mIdMap.clear();
+        mAddressMap.clear();
+        mCachedPerUser.clear();
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 94a97d8..1e50c80 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -159,7 +159,7 @@
 
     // Persistent data store for all Associations.
     private PersistentDataStore mPersistentStore;
-    private AssociationStoreImpl mAssociationStore;
+    private final AssociationStoreImpl mAssociationStore = new AssociationStoreImpl();
     private AssociationRequestsProcessor mAssociationRequestsProcessor;
 
     private PowerWhitelistManager mPowerWhitelistManager;
@@ -219,16 +219,8 @@
     @Override
     public void onStart() {
         mPersistentStore = new PersistentDataStore();
-        final Set<AssociationInfo> allAssociations = new ArraySet<>();
 
-        synchronized (mPreviouslyUsedIds) {
-            // The data is stored in DE directories, so we can read the data for all users now
-            // (which would not be possible if the data was stored to CE directories).
-            mPersistentStore.readStateForUsers(
-                    mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
-        }
-
-        mAssociationStore = new AssociationStoreImpl(allAssociations);
+        loadAssociationsFromDisk();
         mAssociationStore.registerListener(this);
 
         mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
@@ -239,6 +231,18 @@
         publishBinderService(Context.COMPANION_DEVICE_SERVICE, impl);
     }
 
+    void loadAssociationsFromDisk() {
+        final Set<AssociationInfo> allAssociations = new ArraySet<>();
+        synchronized (mPreviouslyUsedIds) {
+            // The data is stored in DE directories, so we can read the data for all users now
+            // (which would not be possible if the data was stored to CE directories).
+            mPersistentStore.readStateForUsers(
+                    mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
+        }
+
+        mAssociationStore.setAssociations(allAssociations);
+    }
+
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
@@ -600,11 +604,13 @@
                 return;
             }
 
-            association.setLastTimeConnected(System.currentTimeMillis());
-            mAssociationStore.updateAssociation(association);
+            AssociationInfo updatedAssociationInfo = AssociationInfo.builder(association)
+                    .setLastTimeConnected(System.currentTimeMillis())
+                    .build();
+            mAssociationStore.updateAssociation(updatedAssociationInfo);
 
             mCompanionDevicePresenceController.onDeviceNotifyAppeared(
-                    association, getContext(), mMainHandler);
+                    updatedAssociationInfo, getContext(), mMainHandler);
         }
 
         @Override
@@ -651,8 +657,10 @@
                         + " for user " + userId));
             }
 
-            association.setNotifyOnDeviceNearby(active);
-            mAssociationStore.updateAssociation(association);
+            AssociationInfo updatedAssociationInfo = AssociationInfo.builder(association)
+                    .setNotifyOnDeviceNearby(active)
+                    .build();
+            mAssociationStore.updateAssociation(updatedAssociationInfo);
         }
 
         @Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index 4447684..fc66817 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -48,7 +48,7 @@
 public class CompanionDevicePresenceController {
     private static final String LOG_TAG = "CompanionDevicePresenceController";
     PerUser<ArrayMap<String, List<BoundService>>> mBoundServices;
-    private static final String META_DATA_KEY_PRIMARY = "primary";
+    private static final String META_DATA_KEY_PRIMARY = "android.companion.primary";
     private final CompanionDeviceManagerService mService;
 
     public CompanionDevicePresenceController(CompanionDeviceManagerService service) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
new file mode 100644
index 0000000..777917c
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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.companion;
+
+import static android.content.Context.BIND_IMPORTANT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceService;
+import android.companion.ICompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.infra.ServiceConnector;
+
+/**
+ * Manages a connection (binding) to an instance of {@link CompanionDeviceService} running in the
+ * application process.
+ */
+@SuppressLint("LongLogTag")
+class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDeviceService> {
+    private static final String TAG = "CompanionDevice_ServiceConnector";
+    private static final boolean DEBUG = false;
+    private static final int BINDING_FLAGS = BIND_IMPORTANT;
+
+    /** Listener for changes to the state of the {@link CompanionDeviceServiceConnector}  */
+    interface Listener {
+        void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
+    }
+
+    private final @UserIdInt int mUserId;
+    private final @NonNull ComponentName mComponentName;
+    private @Nullable Listener mListener;
+
+    CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
+            @NonNull ComponentName componentName) {
+        super(context, buildIntent(componentName), BINDING_FLAGS, userId, null);
+        mUserId = userId;
+        mComponentName = componentName;
+    }
+
+    void setListener(@Nullable Listener listener) {
+        mListener = listener;
+    }
+
+    void postOnDeviceAppeared(@NonNull AssociationInfo associationInfo) {
+        post(companionService -> companionService.onDeviceAppeared(associationInfo));
+    }
+
+    void postOnDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
+        post(companionService -> companionService.onDeviceDisappeared(associationInfo));
+    }
+
+    /**
+     * Post "unbind" job, which will run *after* all previously posted jobs complete.
+     *
+     * IMPORTANT: use this method instead of invoking {@link ServiceConnector#unbind()} directly,
+     * because the latter may cause previously posted callback, such as
+     * {@link ICompanionDeviceService#onDeviceDisappeared(AssociationInfo)} to be dropped.
+     */
+    void postUnbind() {
+        post(it -> unbind());
+    }
+
+    @Override
+    protected void onServiceConnectionStatusChanged(
+            @NonNull ICompanionDeviceService service, boolean isConnected) {
+        if (DEBUG) {
+            Log.d(TAG, "onServiceConnection_StatusChanged() " + mComponentName.toShortString()
+                    + " connected=" + isConnected);
+        }
+    }
+
+    @Override
+    public void onBindingDied(@NonNull ComponentName name) {
+        // IMPORTANT: call super!
+        super.onBindingDied(name);
+
+        if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
+
+        mListener.onBindingDied(mUserId, mComponentName.getPackageName());
+    }
+
+    @Override
+    protected ICompanionDeviceService binderAsInterface(@NonNull IBinder service) {
+        return ICompanionDeviceService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        // Do NOT auto-disconnect.
+        return -1;
+    }
+
+    private static @NonNull Intent buildIntent(@NonNull ComponentName componentName) {
+        return new Intent(CompanionDeviceService.SERVICE_INTERFACE)
+                .setComponent(componentName);
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 5c0571d..5f46d5c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -82,7 +82,10 @@
                     mService.onDeviceDisconnected(getNextArgRequired());
                 }
                 break;
-
+                case "clear-association-memory-cache": {
+                    mService.loadAssociationsFromDisk();
+                }
+                break;
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -110,5 +113,8 @@
         pw.println("      Create a new Association.");
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
+        pw.println("  clear-association-memory-cache");
+        pw.println("      Clear the in-memory association cache and reload all association "
+                + "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
     }
 }
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
new file mode 100644
index 0000000..fcb14a4
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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.companion;
+
+import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
+import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.CompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility methods for working with {@link PackageInfo}-s.
+ */
+final class PackageUtils {
+    private static final Intent COMPANION_SERVICE_INTENT =
+            new Intent(CompanionDeviceService.SERVICE_INTERFACE);
+    private static final String META_DATA_PRIMARY_TAG = "android.companion.primary";
+
+    static @Nullable PackageInfo getPackageInfo(@NonNull Context context,
+            @UserIdInt int userId, @NonNull String packageName) {
+        final PackageManager pm = context.getPackageManager();
+        final PackageInfoFlags flags = PackageInfoFlags.of(GET_PERMISSIONS | GET_CONFIGURATIONS);
+        return Binder.withCleanCallingIdentity(() ->
+                pm.getPackageInfoAsUser(packageName, flags , userId));
+    }
+
+    static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
+            @UserIdInt int userId, @NonNull String packageName) {
+        final boolean requested = ArrayUtils.contains(
+                getPackageInfo(context, userId, packageName).reqFeatures,
+                FEATURE_COMPANION_DEVICE_SETUP);
+
+        if (requested) {
+            throw new IllegalStateException("Must declare uses-feature "
+                    + FEATURE_COMPANION_DEVICE_SETUP
+                    + " in manifest to use this API");
+        }
+    }
+
+    /**
+     * @return list of {@link CompanionDeviceService}-s per package for a given user.
+     *         Services marked as "primary" would always appear at the head of the lists, *before*
+     *         all non-primary services.
+     */
+    static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
+            @NonNull Context context, @UserIdInt int userId) {
+        final PackageManager pm = context.getPackageManager();
+        final ResolveInfoFlags flags = ResolveInfoFlags.of(GET_META_DATA);
+        final List<ResolveInfo> companionServices =
+                pm.queryIntentServicesAsUser(COMPANION_SERVICE_INTENT, flags, userId);
+
+        final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>();
+
+        for (ResolveInfo resolveInfo : companionServices) {
+            final ServiceInfo service = resolveInfo.serviceInfo;
+
+            final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE
+                    .equals(resolveInfo.serviceInfo.permission);
+            if (!requiresPermission) {
+                Slog.w(LOG_TAG, "CompanionDeviceService "
+                        + service.getComponentName().flattenToShortString() + " must require "
+                        + "android.permission.BIND_COMPANION_DEVICE_SERVICE");
+                continue;
+            }
+
+            // Use LinkedList, because we'll need to prepend "primary" services, while appending the
+            // other (non-primary) services to the list.
+            final LinkedList<ComponentName> services =
+                    (LinkedList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent(
+                            service.packageName, it -> new LinkedList<>());
+
+            final ComponentName componentName = service.getComponentName();
+            if (isPrimaryCompanionDeviceService(service)) {
+                // "Primary" service should be at the head of the list.
+                services.addFirst(componentName);
+            } else {
+                services.addLast(componentName);
+            }
+        }
+
+        return packageNameToServiceInfoList;
+    }
+
+    private static boolean isPrimaryCompanionDeviceService(ServiceInfo service) {
+        return service.metaData != null && service.metaData.getBoolean(META_DATA_PRIMARY_TAG);
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index ef3aa7f..3c8c3cb 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -67,17 +67,20 @@
  * The class responsible for persisting Association records and other related information (such as
  * previously used IDs) to a disk, and reading the data back from the disk.
  *
- * Before Android T the data was stored to `companion_device_manager_associations.xml` file in
- * {@link Environment#getUserSystemDirectory(int)}
- * (eg. `/data/system/users/0/companion_device_manager_associations.xml`)
- * @see #getBaseLegacyStorageFileForUser(int)
+ * <p>
+ * Before Android T the data was stored in "companion_device_manager_associations.xml" file in
+ * {@link Environment#getUserSystemDirectory(int) /data/system/user/}.
  *
- * Before Android T the data was stored using the v0 schema.
+ * See {@link #getBaseLegacyStorageFileForUser(int) getBaseLegacyStorageFileForUser()}.
  *
- * @see #readAssociationsV0(TypedXmlPullParser, int, Collection)
- * @see #readAssociationV0(TypedXmlPullParser, int, int, Collection)
+ * <p>
+ * Before Android T the data was stored using the v0 schema. See:
+ * <ul>
+ * <li>{@link #readAssociationsV0(TypedXmlPullParser, int, Collection) readAssociationsV0()}.
+ * <li>{@link #readAssociationV0(TypedXmlPullParser, int, int, Collection) readAssociationV0()}.
+ * </ul>
  *
- * The following snippet is a sample of a the file that is using v0 schema.
+ * The following snippet is a sample of a file that is using v0 schema.
  * <pre>{@code
  * <associations>
  *   <association
@@ -93,24 +96,28 @@
  * </associations>
  * }</pre>
  *
+ * <p>
+ * Since Android T the data is stored to "companion_device_manager.xml" file in
+ * {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
  *
- * Since Android T the data is stored to `companion_device_manager.xml` file in
- * {@link Environment#getDataSystemDeDirectory(int)}.
- * (eg. `/data/system_de/0/companion_device_manager.xml`)
- * @see #getBaseStorageFileForUser(int)
-
+ * See {@link #getBaseStorageFileForUser(int) getBaseStorageFileForUser()}
+ *
+ * <p>
  * Since Android T the data is stored using the v1 schema.
- * In the v1 schema, a list of the previously used IDs is storead along with the association
+ *
+ * In the v1 schema, a list of the previously used IDs is stored along with the association
  * records.
- * V1 schema adds a new optional `display_name` attribute, and makes the `mac_address` attribute
+ *
+ * V1 schema adds a new optional "display_name" attribute, and makes the "mac_address" attribute
  * optional.
+ * <ul>
+ * <li> {@link #CURRENT_PERSISTENCE_VERSION}
+ * <li> {@link #readAssociationsV1(TypedXmlPullParser, int, Collection) readAssociationsV1()}
+ * <li> {@link #readAssociationV1(TypedXmlPullParser, int, Collection) readAssociationV1()}
+ * <li> {@link #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map) readPreviouslyUsedIdsV1()}
+ * </ul>
  *
- * @see #CURRENT_PERSISTENCE_VERSION
- * @see #readAssociationsV1(TypedXmlPullParser, int, Collection)
- * @see #readAssociationV1(TypedXmlPullParser, int, Collection)
- * @see #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map)
- *
- * The following snippet is a sample of a the file that is using v0 schema.
+ * The following snippet is a sample of a file that is using v0 schema.
  * <pre>{@code
  * <state persistence-version="1">
  *     <associations>
@@ -206,6 +213,8 @@
         final AtomicFile file = getStorageFileForUser(userId);
         if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
 
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
         synchronized (file) {
             File legacyBaseFile = null;
             final AtomicFile readFrom;
@@ -269,6 +278,8 @@
 
         final AtomicFile file = getStorageFileForUser(userId);
         if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
         synchronized (file) {
             persistStateToFileLocked(file, associations, previouslyUsedIdsPerPackage);
         }
@@ -329,6 +340,13 @@
         });
     }
 
+    /**
+     * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
+     * user.
+     *
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     */
     private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
         return mUserIdToStorageFile.computeIfAbsent(userId,
                 u -> new AtomicFile(getBaseStorageFileForUser(userId)));
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index e98b63e..734e5c3 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.companion.virtual;
 
+import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
@@ -27,9 +28,10 @@
 import android.content.pm.ActivityInfo;
 import android.os.Build;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
 import android.window.DisplayWindowPolicyController;
 
-import java.util.HashSet;
 import java.util.List;
 
 
@@ -38,6 +40,8 @@
  */
 class GenericWindowPolicyController extends DisplayWindowPolicyController {
 
+    private static final String TAG = "VirtualDeviceManager";
+
     /**
      * If required, allow the secure activity to display on remote device since
      * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
@@ -45,10 +49,13 @@
     @ChangeId
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+    @NonNull private final ArraySet<UserHandle> mAllowedUsers;
 
-    @NonNull final HashSet<Integer> mRunningUids = new HashSet<>();
+    @NonNull final ArraySet<Integer> mRunningUids = new ArraySet<>();
 
-    GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
+    GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
+            @NonNull ArraySet<UserHandle> allowedUsers) {
+        mAllowedUsers = allowedUsers;
         setInterestedWindowFlags(windowFlags, systemWindowFlags);
     }
 
@@ -58,7 +65,7 @@
         final int activityCount = activities.size();
         for (int i = 0; i < activityCount; i++) {
             final ActivityInfo aInfo = activities.get(i);
-            if ((aInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+            if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
                 return false;
             }
         }
@@ -68,12 +75,41 @@
     @Override
     public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
             int systemWindowFlags) {
-        if ((activityInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+        return canContainActivity(activityInfo, windowFlags, systemWindowFlags);
+    }
+
+    @Override
+    public void onTopActivityChanged(ComponentName topActivity, int uid) {
+
+    }
+
+    @Override
+    public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
+        mRunningUids.clear();
+        mRunningUids.addAll(runningUids);
+    }
+
+    /**
+     * Returns true if an app with the given UID has an activity running on the virtual display for
+     * this controller.
+     */
+    boolean containsUid(int uid) {
+        return mRunningUids.contains(uid);
+    }
+
+    private boolean canContainActivity(ActivityInfo activityInfo, int windowFlags,
+            int systemWindowFlags) {
+        if ((activityInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+            return false;
+        }
+        final UserHandle activityUser =
+                UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid);
+        if (!mAllowedUsers.contains(activityUser)) {
+            Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser);
             return false;
         }
         if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
-                activityInfo.packageName,
-                UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid))) {
+                activityInfo.packageName, activityUser)) {
             // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
             if ((windowFlags & FLAG_SECURE) != 0) {
                 return false;
@@ -84,25 +120,4 @@
         }
         return true;
     }
-
-    @Override
-    public void onTopActivityChanged(ComponentName topActivity, int uid) {
-
-    }
-
-    @Override
-    public void onRunningAppsChanged(int[] runningUids) {
-        mRunningUids.clear();
-        for (int i = 0; i < runningUids.length; i++) {
-            mRunningUids.add(runningUids[i]);
-        }
-    }
-
-    /**
-     * Returns true if an app with the given UID has an activity running on the virtual display for
-     * this controller.
-     */
-    boolean containsUid(int uid) {
-        return mRunningUids.contains(uid);
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index ca35e03..0c0ee52 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -16,14 +16,23 @@
 
 package com.android.server.companion.virtual;
 
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.companion.AssociationInfo;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceParams;
 import android.content.Context;
 import android.graphics.Point;
+import android.hardware.display.DisplayManager;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
@@ -32,6 +41,11 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.window.DisplayWindowPolicyController;
 
@@ -46,16 +60,19 @@
 final class VirtualDeviceImpl extends IVirtualDevice.Stub
         implements IBinder.DeathRecipient {
 
+    private static final String TAG = "VirtualDeviceImpl";
     private final Object mVirtualDeviceLock = new Object();
 
     private final Context mContext;
     private final AssociationInfo mAssociationInfo;
+    private final PendingTrampolineCallback mPendingTrampolineCallback;
     private final int mOwnerUid;
     private final InputController mInputController;
     @VisibleForTesting
     final List<Integer> mVirtualDisplayIds = new ArrayList<>();
     private final OnDeviceCloseListener mListener;
     private final IBinder mAppToken;
+    private final VirtualDeviceParams mParams;
 
     /**
      * A mapping from the virtual display ID to its corresponding
@@ -65,17 +82,22 @@
             new SparseArray<>();
 
     VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
-            IBinder token, int ownerUid, OnDeviceCloseListener listener) {
-        this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
+            IBinder token, int ownerUid, OnDeviceCloseListener listener,
+            PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
+        this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener,
+                pendingTrampolineCallback, params);
     }
 
     @VisibleForTesting
     VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
-            int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
+            int ownerUid, InputController inputController, OnDeviceCloseListener listener,
+            PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
         mContext = context;
         mAssociationInfo = associationInfo;
+        mPendingTrampolineCallback = pendingTrampolineCallback;
         mOwnerUid = ownerUid;
         mAppToken = token;
+        mParams = params;
         if (inputController == null) {
             mInputController = new InputController(mVirtualDeviceLock);
         } else {
@@ -89,12 +111,67 @@
         }
     }
 
-    @Override
+    /**
+     * Returns the flags that should be added to any virtual displays created on this virtual
+     * device.
+     */
+    int getBaseVirtualDisplayFlags() {
+        int flags = 0;
+        if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+        }
+        return flags;
+    }
+
+    @Override // Binder call
     public int getAssociationId() {
         return mAssociationInfo.getId();
     }
 
     @Override // Binder call
+    public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
+            ResultReceiver resultReceiver) {
+        if (!mVirtualDisplayIds.contains(displayId)) {
+            throw new SecurityException("Display ID " + displayId
+                    + " not found for this virtual device");
+        }
+        if (pendingIntent.isActivity()) {
+            try {
+                sendPendingIntent(displayId, pendingIntent);
+                resultReceiver.send(Activity.RESULT_OK, null);
+            } catch (PendingIntent.CanceledException e) {
+                Slog.w(TAG, "Pending intent canceled", e);
+                resultReceiver.send(Activity.RESULT_CANCELED, null);
+            }
+        } else {
+            PendingTrampoline pendingTrampoline = new PendingTrampoline(pendingIntent,
+                    resultReceiver, displayId);
+            mPendingTrampolineCallback.startWaitingForPendingTrampoline(pendingTrampoline);
+            try {
+                sendPendingIntent(displayId, pendingIntent);
+            } catch (PendingIntent.CanceledException e) {
+                Slog.w(TAG, "Pending intent canceled", e);
+                resultReceiver.send(Activity.RESULT_CANCELED, null);
+                mPendingTrampolineCallback.stopWaitingForPendingTrampoline(pendingTrampoline);
+            }
+        }
+    }
+
+    private void sendPendingIntent(int displayId, PendingIntent pendingIntent)
+            throws PendingIntent.CanceledException {
+        pendingIntent.send(
+                mContext,
+                /* code= */ 0,
+                /* intent= */ null,
+                /* onFinished= */ null,
+                /* handler= */ null,
+                /* requiredPermission= */ null,
+                ActivityOptions.makeBasic()
+                        .setLaunchDisplayId(displayId)
+                        .toBundle());
+    }
+
+    @Override // Binder call
     public void close() {
         mListener.onClose(mAssociationInfo.getId());
         mAppToken.unlinkToDeath(this, 0);
@@ -250,6 +327,7 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         fout.println("  VirtualDevice: ");
+        fout.println("    mAssociationId: " + mAssociationInfo.getId());
         fout.println("    mVirtualDisplayIds: ");
         synchronized (mVirtualDeviceLock) {
             for (int id : mVirtualDisplayIds) {
@@ -267,11 +345,29 @@
         mVirtualDisplayIds.add(displayId);
         final GenericWindowPolicyController dwpc =
                 new GenericWindowPolicyController(FLAG_SECURE,
-                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+                        SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles());
         mWindowPolicyControllers.put(displayId, dwpc);
         return dwpc;
     }
 
+    private ArraySet<UserHandle> getAllowedUserHandles() {
+        ArraySet<UserHandle> result = new ArraySet<>();
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        for (UserHandle profile : userManager.getAllProfiles()) {
+            int nearbyAppStreamingPolicy = dpm.getNearbyAppStreamingPolicy(profile.getIdentifier());
+            if (nearbyAppStreamingPolicy == NEARBY_STREAMING_ENABLED
+                    || nearbyAppStreamingPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY) {
+                result.add(profile);
+            } else if (nearbyAppStreamingPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
+                if (mParams.getUsersWithMatchingAccounts().contains(profile)) {
+                    result.add(profile);
+                }
+            }
+        }
+        return result;
+    }
+
     void onVirtualDisplayRemovedLocked(int displayId) {
         if (!mVirtualDisplayIds.contains(displayId)) {
             throw new IllegalStateException(
@@ -302,4 +398,58 @@
     interface OnDeviceCloseListener {
         void onClose(int associationId);
     }
+
+    interface PendingTrampolineCallback {
+        /**
+         * Called when the callback should start waiting for the given pending trampoline.
+         * Implementations should try to listen for activity starts associated with the given
+         * {@code pendingTrampoline}, and launch the activity on the display with
+         * {@link PendingTrampoline#mDisplayId}.
+         */
+        void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline);
+
+        /**
+         * Called when the callback should stop waiting for the given pending trampoline. This can
+         * happen, for example, when the pending intent failed to send.
+         */
+        void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline);
+    }
+
+    /**
+     * A data class storing a pending trampoline this device is expecting.
+     */
+    static class PendingTrampoline {
+
+        /**
+         * The original pending intent sent, for which a trampoline activity launch is expected.
+         */
+        final PendingIntent mPendingIntent;
+
+        /**
+         * The result receiver associated with this pending call. {@link Activity#RESULT_OK} will
+         * be sent to the receiver if the trampoline activity was captured successfully.
+         * {@link Activity#RESULT_CANCELED} is sent otherwise.
+         */
+        final ResultReceiver mResultReceiver;
+
+        /**
+         * The display ID to send the captured trampoline activity launch to.
+         */
+        final int mDisplayId;
+
+        private PendingTrampoline(PendingIntent pendingIntent, ResultReceiver resultReceiver,
+                int displayId) {
+            mPendingIntent = pendingIntent;
+            mResultReceiver = resultReceiver;
+            mDisplayId = displayId;
+        }
+
+        @Override
+        public String toString() {
+            return "PendingTrampoline{"
+                    + "pendingIntent=" + mPendingIntent
+                    + ", resultReceiver=" + mResultReceiver
+                    + ", displayId=" + mDisplayId + "}";
+        }
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 0db670e..7e0c2fc 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -16,16 +16,23 @@
 
 package com.android.server.companion.virtual;
 
+import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityOptions;
 import android.companion.AssociationInfo;
 import android.companion.CompanionDeviceManager;
 import android.companion.CompanionDeviceManager.OnAssociationsChangedListener;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
 import android.content.Context;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.ExceptionUtils;
@@ -36,6 +43,9 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceImpl.PendingTrampoline;
+import com.android.server.wm.ActivityInterceptorCallback;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -47,10 +57,12 @@
 public class VirtualDeviceManagerService extends SystemService {
 
     private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "VirtualDeviceManagerService";
+    private static final String TAG = "VirtualDeviceManagerService";
 
     private final Object mVirtualDeviceManagerLock = new Object();
     private final VirtualDeviceManagerImpl mImpl;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
 
     /**
      * Mapping from CDM association IDs to virtual devices. Only one virtual device is allowed for
@@ -79,10 +91,39 @@
         mImpl = new VirtualDeviceManagerImpl();
     }
 
+    private final ActivityInterceptorCallback mActivityInterceptorCallback =
+            new ActivityInterceptorCallback() {
+
+        @Nullable
+        @Override
+        public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+            if (info.callingPackage == null) {
+                return null;
+            }
+            PendingTrampoline pt = mPendingTrampolines.remove(info.callingPackage);
+            if (pt == null) {
+                return null;
+            }
+            pt.mResultReceiver.send(Activity.RESULT_OK, null);
+            ActivityOptions options = info.checkedOptions;
+            if (options == null) {
+                options = ActivityOptions.makeBasic();
+            }
+            return new ActivityInterceptResult(
+                    info.intent, options.setLaunchDisplayId(pt.mDisplayId));
+        }
+    };
+
     @Override
     public void onStart() {
         publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
         publishLocalService(VirtualDeviceManagerInternal.class, new LocalService());
+
+        ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
+                ActivityTaskManagerInternal.class);
+        activityTaskManagerInternal.registerActivityStartInterceptor(
+                VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
+                mActivityInterceptorCallback);
     }
 
     @GuardedBy("mVirtualDeviceManagerLock")
@@ -127,11 +168,15 @@
         }
     }
 
-    class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
+    class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
+            VirtualDeviceImpl.PendingTrampolineCallback {
 
         @Override // Binder call
         public IVirtualDevice createVirtualDevice(
-                IBinder token, String packageName, int associationId) {
+                IBinder token,
+                String packageName,
+                int associationId,
+                @NonNull VirtualDeviceParams params) {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
                     "createVirtualDevice");
@@ -160,7 +205,8 @@
                                     mVirtualDevices.remove(associationId);
                                 }
                             }
-                        });
+                        },
+                        this, params);
                 mVirtualDevices.put(associationInfo.getId(), virtualDevice);
                 return virtualDevice;
             }
@@ -181,7 +227,7 @@
                     }
                 }
             } else {
-                Slog.w(LOG_TAG, "No associations for user " + callingUserId);
+                Slog.w(TAG, "No associations for user " + callingUserId);
             }
             return null;
         }
@@ -192,7 +238,7 @@
             try {
                 return super.onTransact(code, data, reply, flags);
             } catch (Throwable e) {
-                Slog.e(LOG_TAG, "Error during IPC", e);
+                Slog.e(TAG, "Error during IPC", e);
                 throw ExceptionUtils.propagate(e, RemoteException.class);
             }
         }
@@ -201,7 +247,7 @@
         public void dump(@NonNull FileDescriptor fd,
                 @NonNull PrintWriter fout,
                 @Nullable String[] args) {
-            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, fout)) {
                 return;
             }
             fout.println("Created virtual devices: ");
@@ -211,10 +257,24 @@
                 }
             }
         }
+
+        @Override
+        public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
+            PendingTrampoline existing = mPendingTrampolines.put(
+                    pendingTrampoline.mPendingIntent.getCreatorPackage(),
+                    pendingTrampoline);
+            if (existing != null) {
+                existing.mResultReceiver.send(Activity.RESULT_CANCELED, null);
+            }
+        }
+
+        @Override
+        public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) {
+            mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage());
+        }
     }
 
     private final class LocalService extends VirtualDeviceManagerInternal {
-
         @Override
         public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
             synchronized (mVirtualDeviceManagerLock) {
@@ -238,6 +298,11 @@
         }
 
         @Override
+        public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) {
+            return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags();
+        }
+
+        @Override
         public boolean isAppOwnerOfAnyVirtualDevice(int uid) {
             synchronized (mVirtualDeviceManagerLock) {
                 int size = mVirtualDevices.size();
@@ -263,4 +328,42 @@
             return false;
         }
     }
+
+    private static final class PendingTrampolineMap {
+        /**
+         * The maximum duration, in milliseconds, to wait for a trampoline activity launch after
+         * invoking a pending intent.
+         */
+        private static final int TRAMPOLINE_WAIT_MS = 5000;
+
+        private final ConcurrentHashMap<String, PendingTrampoline> mMap = new ConcurrentHashMap<>();
+        private final Handler mHandler;
+
+        PendingTrampolineMap(Handler handler) {
+            mHandler = handler;
+        }
+
+        PendingTrampoline put(
+                @NonNull String packageName, @NonNull PendingTrampoline pendingTrampoline) {
+            PendingTrampoline existing = mMap.put(packageName, pendingTrampoline);
+            mHandler.removeCallbacksAndMessages(existing);
+            mHandler.postDelayed(
+                    () -> {
+                        final String creatorPackage =
+                                pendingTrampoline.mPendingIntent.getCreatorPackage();
+                        if (creatorPackage != null) {
+                            remove(creatorPackage);
+                        }
+                    },
+                    pendingTrampoline,
+                    TRAMPOLINE_WAIT_MS);
+            return existing;
+        }
+
+        PendingTrampoline remove(@NonNull String packageName) {
+            PendingTrampoline pendingTrampoline = mMap.remove(packageName);
+            mHandler.removeCallbacksAndMessages(pendingTrampoline);
+            return pendingTrampoline;
+        }
+    }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7b17162..094ed37 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,9 +100,10 @@
     name: "services.core.unboosted",
     defaults: ["platform_service_defaults"],
     srcs: [
-        ":android.hardware.biometrics.face-V1-java-source",
+        ":android.hardware.biometrics.face-V2-java-source",
         ":statslog-art-java-gen",
         ":statslog-contexthub-java-gen",
+        ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex is ready
         ":services.core-sources",
         ":services.core.protologsrc",
         ":dumpstate_aidl",
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
deleted file mode 100644
index 380b1f3..0000000
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.server;
-
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks
- * whether we need to inform BluetoothManagerService on this change.
- *
- * The information of airplane mode turns on would not be passed to the BluetoothManagerService
- * when Bluetooth is on and Bluetooth is in one of the following situations:
- *   1. Bluetooth A2DP is connected.
- *   2. Bluetooth Hearing Aid profile is connected.
- *   3. Bluetooth LE Audio is connected
- */
-class BluetoothAirplaneModeListener {
-    private static final String TAG = "BluetoothAirplaneModeListener";
-    @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count";
-
-    private static final int MSG_AIRPLANE_MODE_CHANGED = 0;
-
-    @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times
-
-    private final BluetoothManagerService mBluetoothManager;
-    private final BluetoothAirplaneModeHandler mHandler;
-    private BluetoothModeChangeHelper mAirplaneHelper;
-
-    @VisibleForTesting int mToastCount = 0;
-
-    BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) {
-        mBluetoothManager = service;
-
-        mHandler = new BluetoothAirplaneModeHandler(looper);
-        context.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
-                mAirplaneModeObserver);
-    }
-
-    private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
-        @Override
-        public void onChange(boolean unused) {
-            // Post from system main thread to android_io thread.
-            Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED);
-            mHandler.sendMessage(msg);
-        }
-    };
-
-    private class BluetoothAirplaneModeHandler extends Handler {
-        BluetoothAirplaneModeHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_AIRPLANE_MODE_CHANGED:
-                    handleAirplaneModeChange();
-                    break;
-                default:
-                    Log.e(TAG, "Invalid message: " + msg.what);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Call after boot complete
-     */
-    @VisibleForTesting
-    void start(BluetoothModeChangeHelper helper) {
-        Log.i(TAG, "start");
-        mAirplaneHelper = helper;
-        mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
-    }
-
-    @VisibleForTesting
-    boolean shouldPopToast() {
-        if (mToastCount >= MAX_TOAST_COUNT) {
-            return false;
-        }
-        mToastCount++;
-        mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount);
-        return true;
-    }
-
-    @VisibleForTesting
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    void handleAirplaneModeChange() {
-        if (shouldSkipAirplaneModeChange()) {
-            Log.i(TAG, "Ignore airplane mode change");
-            // Airplane mode enabled when Bluetooth is being used for audio/headering aid.
-            // Bluetooth is not disabled in such case, only state is changed to
-            // BLUETOOTH_ON_AIRPLANE mode.
-            mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
-                    BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
-            if (shouldPopToast()) {
-                mAirplaneHelper.showToastMessage();
-            }
-            return;
-        }
-        if (mAirplaneHelper != null) {
-            mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager);
-        }
-    }
-
-    @VisibleForTesting
-    boolean shouldSkipAirplaneModeChange() {
-        if (mAirplaneHelper == null) {
-            return false;
-        }
-        if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn()
-                || !mAirplaneHelper.isMediaProfileConnected()) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
deleted file mode 100644
index 611a37d..0000000
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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 com.android.server;
-
-import android.provider.DeviceConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-
-/**
- * The BluetoothDeviceConfigListener handles system device config change callback and checks
- * whether we need to inform BluetoothManagerService on this change.
- *
- * The information of device config change would not be passed to the BluetoothManagerService
- * when Bluetooth is on and Bluetooth is in one of the following situations:
- *   1. Bluetooth A2DP is connected.
- *   2. Bluetooth Hearing Aid profile is connected.
- */
-class BluetoothDeviceConfigListener {
-    private static final String TAG = "BluetoothDeviceConfigListener";
-
-    private final BluetoothManagerService mService;
-    private final boolean mLogDebug;
-
-    BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
-        mService = service;
-        mLogDebug = logDebug;
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_BLUETOOTH,
-                (Runnable r) -> r.run(),
-                mDeviceConfigChangedListener);
-    }
-
-    private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
-            new DeviceConfig.OnPropertiesChangedListener() {
-                @Override
-                public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                    if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
-                        return;
-                    }
-                    if (mLogDebug) {
-                        ArrayList<String> flags = new ArrayList<>();
-                        for (String name : properties.getKeyset()) {
-                            flags.add(name + "='" + properties.getString(name, "") + "'");
-                        }
-                        Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
-                    }
-                    boolean foundInit = false;
-                    for (String name : properties.getKeyset()) {
-                        if (name.startsWith("INIT_")) {
-                            foundInit = true;
-                            break;
-                        }
-                    }
-                    if (!foundInit) {
-                        return;
-                    }
-                    mService.onInitFlagsChanged();
-                }
-            };
-
-}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
deleted file mode 100644
index 262933d..0000000
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ /dev/null
@@ -1,2962 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static android.os.UserHandle.USER_SYSTEM;
-import static android.permission.PermissionCheckerManager.PERMISSION_HARD_DENIED;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProtoEnums;
-import android.bluetooth.IBluetooth;
-import android.bluetooth.IBluetoothCallback;
-import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothHeadset;
-import android.bluetooth.IBluetoothManager;
-import android.bluetooth.IBluetoothManagerCallback;
-import android.bluetooth.IBluetoothProfileServiceConnection;
-import android.bluetooth.IBluetoothStateChangeCallback;
-import android.bluetooth.IBluetoothLeCallControl;
-import android.content.ActivityNotFoundException;
-import android.content.AttributionSource;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerExemptionManager;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.permission.PermissionManager;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
-import android.util.Log;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
-import com.android.server.pm.UserRestrictionsUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Locale;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-class BluetoothManagerService extends IBluetoothManager.Stub {
-    private static final String TAG = "BluetoothManagerService";
-    private static final boolean DBG = true;
-
-    private static final String BLUETOOTH_PRIVILEGED =
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-
-    private static final int ACTIVE_LOG_MAX_SIZE = 20;
-    private static final int CRASH_LOG_MAX_SIZE = 100;
-
-    private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
-    //Maximum msec to wait for service restart
-    private static final int SERVICE_RESTART_TIME_MS = 400;
-    //Maximum msec to wait for restart due to error
-    private static final int ERROR_RESTART_TIME_MS = 3000;
-    //Maximum msec to delay MESSAGE_USER_SWITCHED
-    private static final int USER_SWITCHED_TIME_MS = 200;
-    // Delay for the addProxy function in msec
-    private static final int ADD_PROXY_DELAY_MS = 100;
-    // Delay for retrying enable and disable in msec
-    private static final int ENABLE_DISABLE_DELAY_MS = 300;
-    private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300;
-    private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400;
-
-    private static final int MESSAGE_ENABLE = 1;
-    private static final int MESSAGE_DISABLE = 2;
-    private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
-    private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
-    private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
-    private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
-    private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
-    private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
-    private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
-    private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60;
-    private static final int MESSAGE_TIMEOUT_BIND = 100;
-    private static final int MESSAGE_TIMEOUT_UNBIND = 101;
-    private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200;
-    private static final int MESSAGE_USER_SWITCHED = 300;
-    private static final int MESSAGE_USER_UNLOCKED = 301;
-    private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
-    private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
-    private static final int MESSAGE_RESTORE_USER_SETTING = 500;
-    private static final int MESSAGE_INIT_FLAGS_CHANGED = 600;
-
-    private static final int RESTORE_SETTING_TO_ON = 1;
-    private static final int RESTORE_SETTING_TO_OFF = 0;
-
-    private static final int MAX_ERROR_RESTART_RETRIES = 6;
-    private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10;
-
-    // Bluetooth persisted setting is off
-    private static final int BLUETOOTH_OFF = 0;
-    // Bluetooth persisted setting is on
-    // and Airplane mode won't affect Bluetooth state at start up
-    private static final int BLUETOOTH_ON_BLUETOOTH = 1;
-    // Bluetooth persisted setting is on
-    // but Airplane mode will affect Bluetooth state at start up
-    // and Airplane mode will have higher priority.
-    @VisibleForTesting
-    static final int BLUETOOTH_ON_AIRPLANE = 2;
-
-    private static final int SERVICE_IBLUETOOTH = 1;
-    private static final int SERVICE_IBLUETOOTHGATT = 2;
-
-    private final Context mContext;
-
-    // Locks are not provided for mName and mAddress.
-    // They are accessed in handler or broadcast receiver, same thread context.
-    private String mAddress;
-    private String mName;
-    private final ContentResolver mContentResolver;
-    private final int mUserId;
-    private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
-    private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
-    private IBinder mBluetoothBinder;
-    private IBluetooth mBluetooth;
-    private IBluetoothGatt mBluetoothGatt;
-    private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
-    private boolean mBinding;
-    private boolean mUnbinding;
-
-    private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
-
-    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
-
-    private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener;
-
-    // used inside handler thread
-    private boolean mQuietEnable = false;
-    private boolean mEnable;
-
-    private static CharSequence timeToLog(long timestamp) {
-        return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp);
-    }
-
-    /**
-     * Used for tracking apps that enabled / disabled Bluetooth.
-     */
-    private class ActiveLog {
-        private int mReason;
-        private String mPackageName;
-        private boolean mEnable;
-        private long mTimestamp;
-
-        ActiveLog(int reason, String packageName, boolean enable, long timestamp) {
-            mReason = reason;
-            mPackageName = packageName;
-            mEnable = enable;
-            mTimestamp = timestamp;
-        }
-
-        public String toString() {
-            return timeToLog(mTimestamp) + (mEnable ? "  Enabled " : " Disabled ")
-                    + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName;
-        }
-
-        void dump(ProtoOutputStream proto) {
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp);
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable);
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName);
-            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason);
-        }
-    }
-
-    private final LinkedList<ActiveLog> mActiveLogs = new LinkedList<>();
-    private final LinkedList<Long> mCrashTimestamps = new LinkedList<>();
-    private int mCrashes;
-    private long mLastEnabledTime;
-
-    // configuration from external IBinder call which is used to
-    // synchronize with broadcast receiver.
-    private boolean mQuietEnableExternal;
-    private boolean mEnableExternal;
-
-    // Map of apps registered to keep BLE scanning on.
-    private Map<IBinder, ClientDeathRecipient> mBleApps =
-            new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
-
-    private int mState;
-    private final BluetoothHandler mHandler;
-    private int mErrorRecoveryRetryCounter;
-    private final int mSystemUiUid;
-
-    private boolean mIsHearingAidProfileSupported;
-
-    private AppOpsManager mAppOps;
-
-    // Save a ProfileServiceConnections object for each of the bound
-    // bluetooth profile services
-    private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
-
-    private final boolean mWirelessConsentRequired;
-
-    private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
-        @Override
-        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
-            Message msg =
-                    mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);
-            mHandler.sendMessage(msg);
-        }
-    };
-
-    private final UserRestrictionsListener mUserRestrictionsListener =
-            new UserRestrictionsListener() {
-                @Override
-                public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
-                        Bundle prevRestrictions) {
-
-                    if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
-                            UserManager.DISALLOW_BLUETOOTH_SHARING)) {
-                        updateOppLauncherComponentState(userId,
-                                newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING));
-                    }
-
-                    // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
-                    if (userId == USER_SYSTEM
-                            && UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
-                            newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
-                        if (userId == USER_SYSTEM && newRestrictions.getBoolean(
-                                UserManager.DISALLOW_BLUETOOTH)) {
-                            updateOppLauncherComponentState(userId, true); // Sharing disallowed
-                            sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED,
-                                    mContext.getPackageName());
-                        } else {
-                            updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
-                                    UserManager.DISALLOW_BLUETOOTH_SHARING));
-                        }
-                    }
-                }
-            };
-
-    @VisibleForTesting
-    public void onInitFlagsChanged() {
-        mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
-        mHandler.sendEmptyMessageDelayed(
-                MESSAGE_INIT_FLAGS_CHANGED,
-                DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
-    }
-
-    public boolean onFactoryReset(AttributionSource attributionSource) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
-                "Need BLUETOOTH_PRIVILEGED permission");
-
-        // Wait for stable state if bluetooth is temporary state.
-        int state = getState();
-        if (state == BluetoothAdapter.STATE_BLE_TURNING_ON
-                || state == BluetoothAdapter.STATE_TURNING_ON
-                || state == BluetoothAdapter.STATE_TURNING_OFF) {
-            if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) {
-                return false;
-            }
-        }
-
-        // Clear registered LE apps to force shut-off Bluetooth
-        clearBleApps();
-        state = getState();
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth == null) {
-                return false;
-            }
-            if (state == BluetoothAdapter.STATE_BLE_ON) {
-                addActiveLog(
-                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
-                        mContext.getPackageName(), false);
-                mBluetooth.onBrEdrDown(attributionSource);
-                return true;
-            } else if (state == BluetoothAdapter.STATE_ON) {
-                addActiveLog(
-                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET,
-                        mContext.getPackageName(), false);
-                mBluetooth.disable(attributionSource);
-                return true;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to shutdown Bluetooth", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onAirplaneModeChanged() {
-        synchronized (this) {
-            if (isBluetoothPersistedStateOn()) {
-                if (isAirplaneModeOn()) {
-                    persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
-                } else {
-                    persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                }
-            }
-
-            int st = BluetoothAdapter.STATE_OFF;
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    st = mBluetooth.getState();
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to call getState", e);
-                return;
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-
-            Slog.d(TAG,
-                    "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
-                            st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
-
-            if (isAirplaneModeOn()) {
-                // Clear registered LE apps to force shut-off
-                clearBleApps();
-
-                // If state is BLE_ON make sure we trigger disableBLE
-                if (st == BluetoothAdapter.STATE_BLE_ON) {
-                    try {
-                        mBluetoothLock.readLock().lock();
-                        if (mBluetooth != null) {
-                            addActiveLog(
-                                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                                    mContext.getPackageName(), false);
-                            mBluetooth.onBrEdrDown(mContext.getAttributionSource());
-                            mEnable = false;
-                            mEnableExternal = false;
-                        }
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to call onBrEdrDown", e);
-                    } finally {
-                        mBluetoothLock.readLock().unlock();
-                    }
-                } else if (st == BluetoothAdapter.STATE_ON) {
-                    sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                            mContext.getPackageName());
-                }
-            } else if (mEnableExternal) {
-                sendEnableMsg(mQuietEnableExternal,
-                        BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
-                        mContext.getPackageName());
-            }
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
-                String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
-                if (DBG) {
-                    Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by "
-                            + mContext.getPackageName());
-                }
-                if (newName != null) {
-                    storeNameAndAddress(newName, null);
-                }
-            } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) {
-                String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS);
-                if (newAddress != null) {
-                    if (DBG) {
-                        Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
-                    }
-                    storeNameAndAddress(null, newAddress);
-                } else {
-                    if (DBG) {
-                        Slog.e(TAG, "No Bluetooth Adapter address parameter found");
-                    }
-                }
-            } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
-                final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
-                if (Settings.Global.BLUETOOTH_ON.equals(name)) {
-                    // The Bluetooth On state may be changed during system restore.
-                    final String prevValue =
-                            intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
-                    final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
-
-                    if (DBG) {
-                        Slog.d(TAG,
-                                "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue
-                                        + ", newValue=" + newValue);
-                    }
-
-                    if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) {
-                        Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING,
-                                newValue.equals("0") ? RESTORE_SETTING_TO_OFF
-                                        : RESTORE_SETTING_TO_ON, 0);
-                        mHandler.sendMessage(msg);
-                    }
-                }
-            } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
-                    || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)
-                    || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) {
-                final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
-                        BluetoothProfile.STATE_CONNECTED);
-                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
-                        && state == BluetoothProfile.STATE_DISCONNECTED
-                        && !mBluetoothModeChangeHelper.isMediaProfileConnected()) {
-                    Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
-                    onInitFlagsChanged();
-                }
-            }
-        }
-    };
-
-    BluetoothManagerService(Context context) {
-        mHandler = new BluetoothHandler(IoThread.get().getLooper());
-
-        mContext = context;
-
-        mWirelessConsentRequired = context.getResources()
-                .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired);
-
-        mCrashes = 0;
-        mBluetooth = null;
-        mBluetoothBinder = null;
-        mBluetoothGatt = null;
-        mBinding = false;
-        mUnbinding = false;
-        mEnable = false;
-        mState = BluetoothAdapter.STATE_OFF;
-        mQuietEnableExternal = false;
-        mEnableExternal = false;
-        mAddress = null;
-        mName = null;
-        mErrorRecoveryRetryCounter = 0;
-        mContentResolver = context.getContentResolver();
-        mUserId = mContentResolver.getUserId();
-        // Observe BLE scan only mode settings change.
-        registerForBleScanModeChange();
-        mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
-        mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
-
-        mIsHearingAidProfileSupported = context.getResources()
-                .getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported);
-
-        // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils
-        String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);
-        if (!TextUtils.isEmpty(value)) {
-            boolean isHearingAidEnabled = Boolean.parseBoolean(value);
-            Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);
-            FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);
-            if (isHearingAidEnabled && !mIsHearingAidProfileSupported) {
-                // Overwrite to enable support by FeatureFlag
-                mIsHearingAidProfileSupported = true;
-            }
-        }
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
-        filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
-        filter.addAction(Intent.ACTION_SETTING_RESTORED);
-        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        mContext.registerReceiver(mReceiver, filter);
-
-        loadStoredNameAndAddress();
-        if (isBluetoothPersistedStateOn()) {
-            if (DBG) {
-                Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
-            }
-            mEnableExternal = true;
-        }
-
-        String airplaneModeRadios =
-                Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
-        if (airplaneModeRadios == null || airplaneModeRadios.contains(
-                Settings.Global.RADIO_BLUETOOTH)) {
-            mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
-                    this, IoThread.get().getLooper(), context);
-        }
-
-        int systemUiUid = -1;
-        // Check if device is configured with no home screen, which implies no SystemUI.
-        boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
-        if (!noHome) {
-            PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
-            systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
-                    MATCH_SYSTEM_ONLY, USER_SYSTEM);
-        }
-        if (systemUiUid >= 0) {
-            Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
-        } else {
-            // Some platforms, such as wearables do not have a system ui.
-            Slog.w(TAG, "Unable to resolve SystemUI's UID.");
-        }
-        mSystemUiUid = systemUiUid;
-    }
-
-    /**
-     *  Returns true if airplane mode is currently on
-     */
-    private boolean isAirplaneModeOn() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
-    private boolean supportBluetoothPersistedState() {
-        return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState);
-    }
-
-    /**
-     *  Returns true if the Bluetooth saved state is "on"
-     */
-    private boolean isBluetoothPersistedStateOn() {
-        if (!supportBluetoothPersistedState()) {
-            return false;
-        }
-        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
-        if (DBG) {
-            Slog.d(TAG, "Bluetooth persisted state: " + state);
-        }
-        return state != BLUETOOTH_OFF;
-    }
-
-    private boolean isBluetoothPersistedStateOnAirplane() {
-        if (!supportBluetoothPersistedState()) {
-            return false;
-        }
-        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
-        if (DBG) {
-            Slog.d(TAG, "Bluetooth persisted state: " + state);
-        }
-        return state == BLUETOOTH_ON_AIRPLANE;
-    }
-
-    /**
-     *  Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
-     */
-    private boolean isBluetoothPersistedStateOnBluetooth() {
-        if (!supportBluetoothPersistedState()) {
-            return false;
-        }
-        return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
-                BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
-    }
-
-    /**
-     *  Save the Bluetooth on/off state
-     */
-    private void persistBluetoothSetting(int value) {
-        if (DBG) {
-            Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
-        }
-        // waive WRITE_SECURE_SETTINGS permission check
-        final long callingIdentity = Binder.clearCallingIdentity();
-        try {
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.BLUETOOTH_ON, value);
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-    }
-
-    /**
-     * Returns true if the Bluetooth Adapter's name and address is
-     * locally cached
-     * @return
-     */
-    private boolean isNameAndAddressSet() {
-        return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0;
-    }
-
-    /**
-     * Retrieve the Bluetooth Adapter's name and address and save it in
-     * in the local cache
-     */
-    private void loadStoredNameAndAddress() {
-        if (DBG) {
-            Slog.d(TAG, "Loading stored name and address");
-        }
-        if (mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
-                && Settings.Secure.getIntForUser(mContentResolver,
-                Settings.Secure.BLUETOOTH_NAME, 0, mUserId)
-                == 0) {
-            // if the valid flag is not set, don't load the address and name
-            if (DBG) {
-                Slog.d(TAG, "invalid bluetooth name and address stored");
-            }
-            return;
-        }
-        mName = Settings.Secure.getStringForUser(
-                mContentResolver, Settings.Secure.BLUETOOTH_NAME, mUserId);
-        mAddress = Settings.Secure.getStringForUser(
-                mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS, mUserId);
-        if (DBG) {
-            Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
-        }
-    }
-
-    /**
-     * Save the Bluetooth name and address in the persistent store.
-     * Only non-null values will be saved.
-     * @param name
-     * @param address
-     */
-    private void storeNameAndAddress(String name, String address) {
-        if (name != null) {
-            Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_NAME, name,
-                    mUserId);
-            mName = name;
-            if (DBG) {
-                Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
-                        mContentResolver, Settings.Secure.BLUETOOTH_NAME,
-                        mUserId));
-            }
-        }
-
-        if (address != null) {
-            Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
-                    address, mUserId);
-            mAddress = address;
-            if (DBG) {
-                Slog.d(TAG,
-                        "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
-                                mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
-                                mUserId));
-            }
-        }
-
-        if ((name != null) && (address != null)) {
-            Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDR_VALID, 1,
-                    mUserId);
-        }
-    }
-
-    public IBluetooth registerAdapter(IBluetoothManagerCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "Callback is null in registerAdapter");
-            return null;
-        }
-        synchronized (mCallbacks) {
-            mCallbacks.register(callback);
-        }
-        return mBluetooth;
-    }
-
-    public void unregisterAdapter(IBluetoothManagerCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "Callback is null in unregisterAdapter");
-            return;
-        }
-        synchronized (mCallbacks) {
-            mCallbacks.unregister(callback);
-        }
-    }
-
-    public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
-            return;
-        }
-        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
-    }
-
-    public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        if (callback == null) {
-            Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
-            return;
-        }
-        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
-    }
-
-    public boolean isEnabled() {
-        return getState() == BluetoothAdapter.STATE_ON;
-    }
-
-    public int getState() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getState(): report OFF for non-active and non system user");
-            return BluetoothAdapter.STATE_OFF;
-        }
-
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                return mBluetooth.getState();
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "getState()", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-        return BluetoothAdapter.STATE_OFF;
-    }
-
-    class ClientDeathRecipient implements IBinder.DeathRecipient {
-        private String mPackageName;
-
-        ClientDeathRecipient(String packageName) {
-            mPackageName = packageName;
-        }
-
-        public void binderDied() {
-            if (DBG) {
-                Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
-            }
-
-            for (Map.Entry<IBinder, ClientDeathRecipient> entry : mBleApps.entrySet()) {
-                IBinder token = entry.getKey();
-                ClientDeathRecipient deathRec = entry.getValue();
-                if (deathRec.equals(this)) {
-                    updateBleAppCount(token, false, mPackageName);
-                    break;
-                }
-            }
-        }
-
-        public String getPackageName() {
-            return mPackageName;
-        }
-    }
-
-    @Override
-    public boolean isBleScanAlwaysAvailable() {
-        if (isAirplaneModeOn() && !mEnable) {
-            return false;
-        }
-        try {
-            return Settings.Global.getInt(mContentResolver,
-                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE) != 0;
-        } catch (SettingNotFoundException e) {
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isHearingAidProfileSupported() {
-        return mIsHearingAidProfileSupported;
-    }
-
-    private boolean isDeviceProvisioned() {
-        return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
-                0) != 0;
-    }
-
-    // Monitor change of BLE scan only mode settings.
-    private void registerForProvisioningStateChange() {
-        ContentObserver contentObserver = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                if (!isDeviceProvisioned()) {
-                    if (DBG) {
-                        Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
-                                + "provisioned");
-                    }
-                    return;
-                }
-                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
-                    Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
-                    onInitFlagsChanged();
-                }
-            }
-        };
-
-        mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
-                contentObserver);
-    }
-
-    // Monitor change of BLE scan only mode settings.
-    private void registerForBleScanModeChange() {
-        ContentObserver contentObserver = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                if (isBleScanAlwaysAvailable()) {
-                    // Nothing to do
-                    return;
-                }
-                // BLE scan is not available.
-                disableBleScanMode();
-                clearBleApps();
-                try {
-                    mBluetoothLock.readLock().lock();
-                    if (mBluetooth != null) {
-                        addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                                mContext.getPackageName(), false);
-                        mBluetooth.onBrEdrDown(mContext.getAttributionSource());
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "error when disabling bluetooth", e);
-                } finally {
-                    mBluetoothLock.readLock().unlock();
-                }
-            }
-        };
-
-        mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false,
-                contentObserver);
-    }
-
-    // Disable ble scan only mode.
-    private void disableBleScanMode() {
-        try {
-            mBluetoothLock.writeLock().lock();
-            if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
-                if (DBG) {
-                    Slog.d(TAG, "Reseting the mEnable flag for clean disable");
-                }
-                mEnable = false;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "getState()", e);
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-    }
-
-    private int updateBleAppCount(IBinder token, boolean enable, String packageName) {
-        ClientDeathRecipient r = mBleApps.get(token);
-        if (r == null && enable) {
-            ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName);
-            try {
-                token.linkToDeath(deathRec, 0);
-            } catch (RemoteException ex) {
-                throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!");
-            }
-            mBleApps.put(token, deathRec);
-            if (DBG) {
-                Slog.d(TAG, "Registered for death of " + packageName);
-            }
-        } else if (!enable && r != null) {
-            // Unregister death recipient as the app goes away.
-            token.unlinkToDeath(r, 0);
-            mBleApps.remove(token);
-            if (DBG) {
-                Slog.d(TAG, "Unregistered for death of " + packageName);
-            }
-        }
-        int appCount = mBleApps.size();
-        if (DBG) {
-            Slog.d(TAG, appCount + " registered Ble Apps");
-        }
-        return appCount;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private boolean checkBluetoothPermissions(AttributionSource attributionSource, String message,
-            boolean requireForeground) {
-        if (isBluetoothDisallowed()) {
-            if (DBG) {
-                Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed");
-            }
-            return false;
-        }
-        // Check if packageName belongs to callingUid
-        final int callingUid = Binder.getCallingUid();
-        final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!isCallerSystem) {
-            checkPackage(callingUid, attributionSource.getPackageName());
-
-            if (requireForeground && !checkIfCallerIsForegroundUser()) {
-                Slog.w(TAG, "Not allowed for non-active and non system user");
-                return false;
-            }
-
-            if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, message)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public boolean enableBle(AttributionSource attributionSource, IBinder token)
-            throws RemoteException {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "enableBle", false)) {
-            if (DBG) {
-                Slog.d(TAG, "enableBle(): bluetooth disallowed");
-            }
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "enableBle(" + packageName + "):  mBluetooth =" + mBluetooth
-                    + " mBinding = " + mBinding + " mState = "
-                    + BluetoothAdapter.nameForState(mState));
-        }
-        updateBleAppCount(token, true, packageName);
-
-        if (mState == BluetoothAdapter.STATE_ON
-                || mState == BluetoothAdapter.STATE_BLE_ON
-                || mState == BluetoothAdapter.STATE_TURNING_ON
-                || mState == BluetoothAdapter.STATE_TURNING_OFF
-                || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) {
-            Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on");
-            return true;
-        }
-        synchronized (mReceiver) {
-            // waive WRITE_SECURE_SETTINGS permission check
-            sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                    packageName, true);
-        }
-        return true;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean disableBle(AttributionSource attributionSource, IBinder token)
-            throws RemoteException {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "disableBle", false)) {
-            if (DBG) {
-                Slog.d(TAG, "disableBLE(): bluetooth disallowed");
-            }
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "disableBle(" + packageName + "):  mBluetooth =" + mBluetooth
-                    + " mBinding = " + mBinding + " mState = "
-                    + BluetoothAdapter.nameForState(mState));
-        }
-
-        if (mState == BluetoothAdapter.STATE_OFF) {
-            Slog.d(TAG, "disableBLE(): Already disabled");
-            return false;
-        }
-        updateBleAppCount(token, false, packageName);
-
-        if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) {
-            if (mEnable) {
-                disableBleScanMode();
-            }
-            if (!mEnableExternal) {
-                addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                        packageName, false);
-                sendBrEdrDownCallback(attributionSource);
-            }
-        }
-        return true;
-    }
-
-    // Clear all apps using BLE scan only mode.
-    private void clearBleApps() {
-        mBleApps.clear();
-    }
-
-    /** @hide */
-    public boolean isBleAppPresent() {
-        if (DBG) {
-            Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
-        }
-        return mBleApps.size() > 0;
-    }
-
-    /**
-     * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on,
-     * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off.
-     */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    private void continueFromBleOnState() {
-        if (DBG) {
-            Slog.d(TAG, "continueFromBleOnState()");
-        }
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth == null) {
-                Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!");
-                return;
-            }
-            if (!mEnableExternal && !isBleAppPresent()) {
-                Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now");
-                mEnable = false;
-                mBluetooth.onBrEdrDown(mContext.getAttributionSource());
-                return;
-            }
-            if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
-                // This triggers transition to STATE_ON
-                mBluetooth.onLeServiceUp(mContext.getAttributionSource());
-                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to call onServiceUp", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-    }
-
-    /**
-     * Inform BluetoothAdapter instances that BREDR part is down
-     * and turn off all service and stack if no LE app needs it
-     */
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    private void sendBrEdrDownCallback(AttributionSource attributionSource) {
-        if (DBG) {
-            Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks");
-        }
-
-        if (mBluetooth == null) {
-            Slog.w(TAG, "Bluetooth handle is null");
-            return;
-        }
-
-        if (isBleAppPresent()) {
-            // Need to stay at BLE ON. Disconnect all Gatt connections
-            try {
-                mBluetoothGatt.unregAll(attributionSource);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to disconnect all apps.", e);
-            }
-        } else {
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    mBluetooth.onBrEdrDown(attributionSource);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-        }
-
-    }
-
-    public boolean enableNoAutoConnect(AttributionSource attributionSource) {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "enableNoAutoConnect", false)) {
-            if (DBG) {
-                Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed");
-            }
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "enableNoAutoConnect():  mBluetooth =" + mBluetooth + " mBinding = "
-                    + mBinding);
-        }
-
-        int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
-        if (callingAppId != Process.NFC_UID) {
-            throw new SecurityException("no permission to enable Bluetooth quietly");
-        }
-
-        synchronized (mReceiver) {
-            mQuietEnableExternal = true;
-            mEnableExternal = true;
-            sendEnableMsg(true,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
-        }
-        return true;
-    }
-
-    public boolean enable(AttributionSource attributionSource) throws RemoteException {
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "enable", true)) {
-            if (DBG) {
-                Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
-            }
-            return false;
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!callerSystem && !isEnabled() && mWirelessConsentRequired
-                && startConsentUiIfNeeded(packageName,
-                callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "enable(" + packageName + "):  mBluetooth =" + mBluetooth + " mBinding = "
-                    + mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
-        }
-
-        synchronized (mReceiver) {
-            mQuietEnableExternal = false;
-            mEnableExternal = true;
-            // waive WRITE_SECURE_SETTINGS permission check
-            sendEnableMsg(false,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
-        }
-        if (DBG) {
-            Slog.d(TAG, "enable returning");
-        }
-        return true;
-    }
-
-    public boolean disable(AttributionSource attributionSource, boolean persist)
-            throws RemoteException {
-        if (!persist) {
-            mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
-                    "Need BLUETOOTH_PRIVILEGED permission");
-        }
-
-        final String packageName = attributionSource.getPackageName();
-        if (!checkBluetoothPermissions(attributionSource, "disable", true)) {
-            if (DBG) {
-                Slog.d(TAG, "disable(): not disabling - bluetooth disallowed");
-            }
-            return false;
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!callerSystem && isEnabled() && mWirelessConsentRequired
-                && startConsentUiIfNeeded(packageName,
-                callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
-            return false;
-        }
-
-        if (DBG) {
-            Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding);
-        }
-
-        synchronized (mReceiver) {
-            if (!isBluetoothPersistedStateOnAirplane()) {
-                if (persist) {
-                    persistBluetoothSetting(BLUETOOTH_OFF);
-                }
-                mEnableExternal = false;
-            }
-            sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
-                    packageName);
-        }
-        return true;
-    }
-
-    private boolean startConsentUiIfNeeded(String packageName,
-            int callingUid, String intentAction) throws RemoteException {
-        if (checkBluetoothPermissionWhenWirelessConsentRequired()) {
-            return false;
-        }
-        try {
-            // Validate the package only if we are going to use it
-            ApplicationInfo applicationInfo = mContext.getPackageManager()
-                    .getApplicationInfoAsUser(packageName,
-                            PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                            UserHandle.getUserId(callingUid));
-            if (applicationInfo.uid != callingUid) {
-                throw new SecurityException("Package " + packageName
-                        + " not in uid " + callingUid);
-            }
-
-            Intent intent = new Intent(intentAction);
-            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            try {
-                mContext.startActivity(intent);
-            } catch (ActivityNotFoundException e) {
-                // Shouldn't happen
-                Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
-                return false;
-            }
-            return true;
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RemoteException(e.getMessage());
-        }
-    }
-
-    /**
-     * Check if AppOpsManager is available and the packageName belongs to uid
-     *
-     * A null package belongs to any uid
-     */
-    private void checkPackage(int uid, String packageName) {
-        if (mAppOps == null) {
-            Slog.w(TAG, "checkPackage(): called before system boot up, uid "
-                    + uid + ", packageName " + packageName);
-            throw new IllegalStateException("System has not boot yet");
-        }
-        if (packageName == null) {
-            Slog.w(TAG, "checkPackage(): called with null packageName from " + uid);
-            return;
-        }
-        try {
-            mAppOps.checkPackage(uid, packageName);
-        } catch (SecurityException e) {
-            Slog.w(TAG, "checkPackage(): " + packageName + " does not belong to uid " + uid);
-            throw new SecurityException(e.getMessage());
-        }
-    }
-
-    /**
-     * Check if the caller must still pass permission check or if the caller is exempted
-     * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check.
-     *
-     * Commands from some callers may be exempted from triggering the consent UI when
-     * enabling bluetooth. This exemption is checked via the
-     * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip
-     * the consent UI where it may otherwise be required.
-     *
-     * @hide
-     */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private boolean checkBluetoothPermissionWhenWirelessConsentRequired() {
-        int result = mContext.checkCallingPermission(
-                android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED);
-        return result == PackageManager.PERMISSION_GRANTED;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void unbindAndFinish() {
-        if (DBG) {
-            Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding
-                    + " mUnbinding = " + mUnbinding);
-        }
-
-        try {
-            mBluetoothLock.writeLock().lock();
-            if (mUnbinding) {
-                return;
-            }
-            mUnbinding = true;
-            mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-            mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE);
-            if (mBluetooth != null) {
-                //Unregister callback object
-                try {
-                    mBluetooth.unregisterCallback(mBluetoothCallback,
-                            mContext.getAttributionSource());
-                } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to unregister BluetoothCallback", re);
-                }
-                mBluetoothBinder = null;
-                mBluetooth = null;
-                mContext.unbindService(mConnection);
-                mUnbinding = false;
-                mBinding = false;
-            } else {
-                mUnbinding = false;
-            }
-            mBluetoothGatt = null;
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-    }
-
-    public IBluetoothGatt getBluetoothGatt() {
-        // sync protection
-        return mBluetoothGatt;
-    }
-
-    @Override
-    public boolean bindBluetoothProfileService(int bluetoothProfile,
-            IBluetoothProfileServiceConnection proxy) {
-        if (mState != BluetoothAdapter.STATE_ON) {
-            if (DBG) {
-                Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile
-                        + ", while Bluetooth was disabled");
-            }
-            return false;
-        }
-        synchronized (mProfileServices) {
-            ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
-            if (psc == null) {
-                if (DBG) {
-                    Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: "
-                            + bluetoothProfile);
-                }
-
-                Intent intent;
-                if (bluetoothProfile == BluetoothProfile.HEADSET) {
-                    intent = new Intent(IBluetoothHeadset.class.getName());
-                } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
-                    intent = new Intent(IBluetoothLeCallControl.class.getName());
-                } else {
-                    return false;
-                }
-
-                psc = new ProfileServiceConnections(intent);
-                if (!psc.bindService()) {
-                    return false;
-                }
-
-                mProfileServices.put(new Integer(bluetoothProfile), psc);
-            }
-        }
-
-        // Introducing a delay to give the client app time to prepare
-        Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED);
-        addProxyMsg.arg1 = bluetoothProfile;
-        addProxyMsg.obj = proxy;
-        mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS);
-        return true;
-    }
-
-    @Override
-    public void unbindBluetoothProfileService(int bluetoothProfile,
-            IBluetoothProfileServiceConnection proxy) {
-        synchronized (mProfileServices) {
-            Integer profile = new Integer(bluetoothProfile);
-            ProfileServiceConnections psc = mProfileServices.get(profile);
-            if (psc == null) {
-                return;
-            }
-            psc.removeProxy(proxy);
-            if (psc.isEmpty()) {
-                // All prxoies are disconnected, unbind with the service.
-                try {
-                    mContext.unbindService(psc);
-                } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
-                }
-                mProfileServices.remove(profile);
-            }
-        }
-    }
-
-    private void unbindAllBluetoothProfileServices() {
-        synchronized (mProfileServices) {
-            for (Integer i : mProfileServices.keySet()) {
-                ProfileServiceConnections psc = mProfileServices.get(i);
-                try {
-                    mContext.unbindService(psc);
-                } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
-                }
-                psc.removeAllProxies();
-            }
-            mProfileServices.clear();
-        }
-    }
-
-    /**
-     * Send enable message and set adapter name and address. Called when the boot phase becomes
-     * PHASE_SYSTEM_SERVICES_READY.
-     */
-    public void handleOnBootPhase() {
-        if (DBG) {
-            Slog.d(TAG, "Bluetooth boot completed");
-        }
-        mAppOps = mContext.getSystemService(AppOpsManager.class);
-        UserManagerInternal userManagerInternal =
-                LocalServices.getService(UserManagerInternal.class);
-        userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
-        final boolean isBluetoothDisallowed = isBluetoothDisallowed();
-        if (isBluetoothDisallowed) {
-            return;
-        }
-        final boolean isSafeMode = mContext.getPackageManager().isSafeMode();
-        if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) {
-            if (DBG) {
-                Slog.d(TAG, "Auto-enabling Bluetooth.");
-            }
-            sendEnableMsg(mQuietEnableExternal,
-                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT,
-                    mContext.getPackageName());
-        } else if (!isNameAndAddressSet()) {
-            if (DBG) {
-                Slog.d(TAG, "Getting adapter name and address");
-            }
-            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
-            mHandler.sendMessage(getMsg);
-        }
-
-        mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
-        if (mBluetoothAirplaneModeListener != null) {
-            mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
-        }
-        registerForProvisioningStateChange();
-        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
-    }
-
-    /**
-     * Called when switching to a different foreground user.
-     */
-    public void handleOnSwitchUser(int userHandle) {
-        if (DBG) {
-            Slog.d(TAG, "User " + userHandle + " switched");
-        }
-        mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget();
-    }
-
-    /**
-     * Called when user is unlocked.
-     */
-    public void handleOnUnlockUser(int userHandle) {
-        if (DBG) {
-            Slog.d(TAG, "User " + userHandle + " unlocked");
-        }
-        mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget();
-    }
-
-    /**
-     * This class manages the clients connected to a given ProfileService
-     * and maintains the connection with that service.
-     */
-    private final class ProfileServiceConnections
-            implements ServiceConnection, IBinder.DeathRecipient {
-        final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
-                new RemoteCallbackList<IBluetoothProfileServiceConnection>();
-        IBinder mService;
-        ComponentName mClassName;
-        Intent mIntent;
-        boolean mInvokingProxyCallbacks = false;
-
-        ProfileServiceConnections(Intent intent) {
-            mService = null;
-            mClassName = null;
-            mIntent = intent;
-        }
-
-        private boolean bindService() {
-            int state = BluetoothAdapter.STATE_OFF;
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    state = mBluetooth.getState();
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to call getState", e);
-                return false;
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-
-            if (state != BluetoothAdapter.STATE_ON) {
-                if (DBG) {
-                    Slog.d(TAG, "Unable to bindService while Bluetooth is disabled");
-                }
-                return false;
-            }
-
-            if (mIntent != null && mService == null && doBind(mIntent, this, 0,
-                    UserHandle.CURRENT_OR_SELF)) {
-                Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
-                msg.obj = this;
-                mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
-                return true;
-            }
-            Slog.w(TAG, "Unable to bind with intent: " + mIntent);
-            return false;
-        }
-
-        private void addProxy(IBluetoothProfileServiceConnection proxy) {
-            mProxies.register(proxy);
-            if (mService != null) {
-                try {
-                    proxy.onServiceConnected(mClassName, mService);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to connect to proxy", e);
-                }
-            } else {
-                if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
-                    Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
-                    msg.obj = this;
-                    mHandler.sendMessage(msg);
-                }
-            }
-        }
-
-        private void removeProxy(IBluetoothProfileServiceConnection proxy) {
-            if (proxy != null) {
-                if (mProxies.unregister(proxy)) {
-                    try {
-                        proxy.onServiceDisconnected(mClassName);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to disconnect proxy", e);
-                    }
-                }
-            } else {
-                Slog.w(TAG, "Trying to remove a null proxy");
-            }
-        }
-
-        private void removeAllProxies() {
-            onServiceDisconnected(mClassName);
-            mProxies.kill();
-        }
-
-        private boolean isEmpty() {
-            return mProxies.getRegisteredCallbackCount() == 0;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            // remove timeout message
-            mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this);
-            mService = service;
-            mClassName = className;
-            try {
-                mService.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to linkToDeath", e);
-            }
-
-            if (mInvokingProxyCallbacks) {
-                Slog.e(TAG, "Proxy callbacks already in progress.");
-                return;
-            }
-            mInvokingProxyCallbacks = true;
-
-            final int n = mProxies.beginBroadcast();
-            try {
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mProxies.getBroadcastItem(i).onServiceConnected(className, service);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to connect to proxy", e);
-                    }
-                }
-            } finally {
-                mProxies.finishBroadcast();
-                mInvokingProxyCallbacks = false;
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            if (mService == null) {
-                return;
-            }
-            try {
-                mService.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                Log.e(TAG, "error unlinking to death", e);
-            }
-            mService = null;
-            mClassName = null;
-
-            if (mInvokingProxyCallbacks) {
-                Slog.e(TAG, "Proxy callbacks already in progress.");
-                return;
-            }
-            mInvokingProxyCallbacks = true;
-
-            final int n = mProxies.beginBroadcast();
-            try {
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mProxies.getBroadcastItem(i).onServiceDisconnected(className);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to disconnect from proxy", e);
-                    }
-                }
-            } finally {
-                mProxies.finishBroadcast();
-                mInvokingProxyCallbacks = false;
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            if (DBG) {
-                Slog.w(TAG, "Profile service for profile: " + mClassName + " died.");
-            }
-            onServiceDisconnected(mClassName);
-            // Trigger rebind
-            Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
-            msg.obj = this;
-            mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
-        }
-    }
-
-    private void sendBluetoothStateCallback(boolean isUp) {
-        try {
-            int n = mStateChangeCallbacks.beginBroadcast();
-            if (DBG) {
-                Slog.d(TAG, "Broadcasting onBluetoothStateChange(" + isUp + ") to " + n
-                        + " receivers.");
-            }
-            for (int i = 0; i < n; i++) {
-                try {
-                    mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i, e);
-                }
-            }
-        } finally {
-            mStateChangeCallbacks.finishBroadcast();
-        }
-    }
-
-    /**
-     * Inform BluetoothAdapter instances that Adapter service is up
-     */
-    private void sendBluetoothServiceUpCallback() {
-        synchronized (mCallbacks) {
-            try {
-                int n = mCallbacks.beginBroadcast();
-                Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
-                    }
-                }
-            } finally {
-                mCallbacks.finishBroadcast();
-            }
-        }
-    }
-
-    /**
-     * Inform BluetoothAdapter instances that Adapter service is down
-     */
-    private void sendBluetoothServiceDownCallback() {
-        synchronized (mCallbacks) {
-            try {
-                int n = mCallbacks.beginBroadcast();
-                Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
-                for (int i = 0; i < n; i++) {
-                    try {
-                        mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
-                    }
-                }
-            } finally {
-                mCallbacks.finishBroadcast();
-            }
-        }
-    }
-
-    public String getAddress(AttributionSource attributionSource) {
-        if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) {
-            return null;
-        }
-
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getAddress(): not allowed for non-active and non system user");
-            return null;
-        }
-
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS)
-                != PackageManager.PERMISSION_GRANTED) {
-            return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
-        }
-
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                return mBluetooth.getAddressWithAttribution(attributionSource);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG,
-                    "getAddress(): Unable to retrieve address remotely. Returning cached address",
-                    e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-
-        // mAddress is accessed from outside.
-        // It is alright without a lock. Here, bluetooth is off, no other thread is
-        // changing mAddress
-        return mAddress;
-    }
-
-    public String getName(AttributionSource attributionSource) {
-        if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) {
-            return null;
-        }
-
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getName(): not allowed for non-active and non system user");
-            return null;
-        }
-
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                return mBluetooth.getName(attributionSource);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-
-        // mName is accessed from outside.
-        // It alright without a lock. Here, bluetooth is off, no other thread is
-        // changing mName
-        return mName;
-    }
-
-    private class BluetoothServiceConnection implements ServiceConnection {
-        public void onServiceConnected(ComponentName componentName, IBinder service) {
-            String name = componentName.getClassName();
-            if (DBG) {
-                Slog.d(TAG, "BluetoothServiceConnection: " + name);
-            }
-            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
-            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
-                msg.arg1 = SERVICE_IBLUETOOTH;
-            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
-                msg.arg1 = SERVICE_IBLUETOOTHGATT;
-            } else {
-                Slog.e(TAG, "Unknown service connected: " + name);
-                return;
-            }
-            msg.obj = service;
-            mHandler.sendMessage(msg);
-        }
-
-        public void onServiceDisconnected(ComponentName componentName) {
-            // Called if we unexpectedly disconnect.
-            String name = componentName.getClassName();
-            if (DBG) {
-                Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
-            }
-            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
-            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
-                msg.arg1 = SERVICE_IBLUETOOTH;
-            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
-                msg.arg1 = SERVICE_IBLUETOOTHGATT;
-            } else {
-                Slog.e(TAG, "Unknown service disconnected: " + name);
-                return;
-            }
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
-
-    private class BluetoothHandler extends Handler {
-        boolean mGetNameAddressOnly = false;
-        private int mWaitForEnableRetry;
-        private int mWaitForDisableRetry;
-
-        BluetoothHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_GET_NAME_AND_ADDRESS:
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
-                    }
-                    try {
-                        mBluetoothLock.writeLock().lock();
-                        if ((mBluetooth == null) && (!mBinding)) {
-                            if (DBG) {
-                                Slog.d(TAG, "Binding to service to get name and address");
-                            }
-                            mGetNameAddressOnly = true;
-                            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
-                            mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
-                            Intent i = new Intent(IBluetooth.class.getName());
-                            if (!doBind(i, mConnection,
-                                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                                    UserHandle.CURRENT)) {
-                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-                            } else {
-                                mBinding = true;
-                            }
-                        } else if (mBluetooth != null) {
-                            try {
-                                storeNameAndAddress(
-                                        mBluetooth.getName(mContext.getAttributionSource()),
-                                        mBluetooth.getAddressWithAttribution(
-                                                mContext.getAttributionSource()));
-                            } catch (RemoteException re) {
-                                Slog.e(TAG, "Unable to grab names", re);
-                            }
-                            if (mGetNameAddressOnly && !mEnable) {
-                                unbindAndFinish();
-                            }
-                            mGetNameAddressOnly = false;
-                        }
-                    } finally {
-                        mBluetoothLock.writeLock().unlock();
-                    }
-                    break;
-
-                case MESSAGE_ENABLE:
-                    int quietEnable = msg.arg1;
-                    int isBle  = msg.arg2;
-                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
-                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
-                        // We are handling enable or disable right now, wait for it.
-                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
-                                quietEnable, isBle), ENABLE_DISABLE_DELAY_MS);
-                        break;
-                    }
-
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = "
-                                + mBluetooth);
-                    }
-                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                    mEnable = true;
-
-                    if (isBle == 0) {
-                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                    }
-
-                    // Use service interface to get the exact state
-                    try {
-                        mBluetoothLock.readLock().lock();
-                        if (mBluetooth != null) {
-                            boolean isHandled = true;
-                            int state = mBluetooth.getState();
-                            switch (state) {
-                                case BluetoothAdapter.STATE_BLE_ON:
-                                    if (isBle == 1) {
-                                        Slog.i(TAG, "Already at BLE_ON State");
-                                    } else {
-                                        Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
-                                        mBluetooth.onLeServiceUp(mContext.getAttributionSource());
-                                    }
-                                    break;
-                                case BluetoothAdapter.STATE_BLE_TURNING_ON:
-                                case BluetoothAdapter.STATE_TURNING_ON:
-                                case BluetoothAdapter.STATE_ON:
-                                    Slog.i(TAG, "MESSAGE_ENABLE: already enabled");
-                                    break;
-                                default:
-                                    isHandled = false;
-                                    break;
-                            }
-                            if (isHandled) break;
-                        }
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "", e);
-                    } finally {
-                        mBluetoothLock.readLock().unlock();
-                    }
-
-                    mQuietEnable = (quietEnable == 1);
-                    if (mBluetooth == null) {
-                        handleEnable(mQuietEnable);
-                    } else {
-                        //
-                        // We need to wait until transitioned to STATE_OFF and
-                        // the previous Bluetooth process has exited. The
-                        // waiting period has three components:
-                        // (a) Wait until the local state is STATE_OFF. This
-                        //     is accomplished by sending delay a message
-                        //     MESSAGE_HANDLE_ENABLE_DELAYED
-                        // (b) Wait until the STATE_OFF state is updated to
-                        //     all components.
-                        // (c) Wait until the Bluetooth process exits, and
-                        //     ActivityManager detects it.
-                        // The waiting for (b) and (c) is accomplished by
-                        // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE
-                        // message. The delay time is backed off if Bluetooth
-                        // continuously failed to turn on itself.
-                        //
-                        mWaitForEnableRetry = 0;
-                        Message enableDelayedMsg =
-                                mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
-                        mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                    }
-                    break;
-
-                case MESSAGE_DISABLE:
-                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding
-                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
-                        // We are handling enable or disable right now, wait for it.
-                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE),
-                                ENABLE_DISABLE_DELAY_MS);
-                        break;
-                    }
-
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth
-                                + ", mBinding = " + mBinding);
-                    }
-                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-
-                    if (mEnable && mBluetooth != null) {
-                        mWaitForDisableRetry = 0;
-                        Message disableDelayedMsg =
-                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
-                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                    } else {
-                        mEnable = false;
-                        handleDisable();
-                    }
-                    break;
-
-                case MESSAGE_HANDLE_ENABLE_DELAYED: {
-                    // The Bluetooth is turning off, wait for STATE_OFF
-                    if (mState != BluetoothAdapter.STATE_OFF) {
-                        if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
-                            mWaitForEnableRetry++;
-                            Message enableDelayedMsg =
-                                    mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
-                            mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                            break;
-                        } else {
-                            Slog.e(TAG, "Wait for STATE_OFF timeout");
-                        }
-                    }
-                    // Either state is changed to STATE_OFF or reaches the maximum retry, we
-                    // should move forward to the next step.
-                    mWaitForEnableRetry = 0;
-                    Message restartMsg =
-                            mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                    mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
-                    Slog.d(TAG, "Handle enable is finished");
-                    break;
-                }
-
-                case MESSAGE_HANDLE_DISABLE_DELAYED: {
-                    boolean disabling = (msg.arg1 == 1);
-                    Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling);
-                    if (!disabling) {
-                        // The Bluetooth is turning on, wait for STATE_ON
-                        if (mState != BluetoothAdapter.STATE_ON) {
-                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
-                                mWaitForDisableRetry++;
-                                Message disableDelayedMsg = mHandler.obtainMessage(
-                                        MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
-                                mHandler.sendMessageDelayed(disableDelayedMsg,
-                                        ENABLE_DISABLE_DELAY_MS);
-                                break;
-                            } else {
-                                Slog.e(TAG, "Wait for STATE_ON timeout");
-                            }
-                        }
-                        // Either state is changed to STATE_ON or reaches the maximum retry, we
-                        // should move forward to the next step.
-                        mWaitForDisableRetry = 0;
-                        mEnable = false;
-                        handleDisable();
-                        // Wait for state exiting STATE_ON
-                        Message disableDelayedMsg =
-                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
-                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
-                    } else {
-                        // The Bluetooth is turning off, wait for exiting STATE_ON
-                        if (mState == BluetoothAdapter.STATE_ON) {
-                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
-                                mWaitForDisableRetry++;
-                                Message disableDelayedMsg = mHandler.obtainMessage(
-                                        MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
-                                mHandler.sendMessageDelayed(disableDelayedMsg,
-                                        ENABLE_DISABLE_DELAY_MS);
-                                break;
-                            } else {
-                                Slog.e(TAG, "Wait for exiting STATE_ON timeout");
-                            }
-                        }
-                        // Either state is exited from STATE_ON or reaches the maximum retry, we
-                        // should move forward to the next step.
-                        Slog.d(TAG, "Handle disable is finished");
-                    }
-                    break;
-                }
-
-                case MESSAGE_RESTORE_USER_SETTING:
-                    if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
-                        if (DBG) {
-                            Slog.d(TAG, "Restore Bluetooth state to disabled");
-                        }
-                        persistBluetoothSetting(BLUETOOTH_OFF);
-                        mEnableExternal = false;
-                        sendDisableMsg(
-                                BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING,
-                                mContext.getPackageName());
-                    } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) {
-                        if (DBG) {
-                            Slog.d(TAG, "Restore Bluetooth state to enabled");
-                        }
-                        mQuietEnableExternal = false;
-                        mEnableExternal = true;
-                        // waive WRITE_SECURE_SETTINGS permission check
-                        sendEnableMsg(false,
-                                BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING,
-                                mContext.getPackageName());
-                    }
-                    break;
-                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
-                    IBluetoothStateChangeCallback callback =
-                            (IBluetoothStateChangeCallback) msg.obj;
-                    mStateChangeCallbacks.register(callback);
-                    break;
-                }
-                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: {
-                    IBluetoothStateChangeCallback callback =
-                            (IBluetoothStateChangeCallback) msg.obj;
-                    mStateChangeCallbacks.unregister(callback);
-                    break;
-                }
-                case MESSAGE_ADD_PROXY_DELAYED: {
-                    ProfileServiceConnections psc = mProfileServices.get(msg.arg1);
-                    if (psc == null) {
-                        break;
-                    }
-                    IBluetoothProfileServiceConnection proxy =
-                            (IBluetoothProfileServiceConnection) msg.obj;
-                    psc.addProxy(proxy);
-                    break;
-                }
-                case MESSAGE_BIND_PROFILE_SERVICE: {
-                    ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
-                    removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
-                    if (psc == null) {
-                        break;
-                    }
-                    psc.bindService();
-                    break;
-                }
-                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
-                    }
-
-                    IBinder service = (IBinder) msg.obj;
-                    try {
-                        mBluetoothLock.writeLock().lock();
-                        if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
-                            mBluetoothGatt =
-                                    IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));
-                            continueFromBleOnState();
-                            break;
-                        } // else must be SERVICE_IBLUETOOTH
-
-                        //Remove timeout
-                        mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-
-                        mBinding = false;
-                        mBluetoothBinder = service;
-                        mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));
-
-                        if (!isNameAndAddressSet()) {
-                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
-                            mHandler.sendMessage(getMsg);
-                            if (mGetNameAddressOnly) {
-                                return;
-                            }
-                        }
-
-                        //Register callback object
-                        try {
-                            mBluetooth.registerCallback(mBluetoothCallback,
-                                    mContext.getAttributionSource());
-                        } catch (RemoteException re) {
-                            Slog.e(TAG, "Unable to register BluetoothCallback", re);
-                        }
-                        //Inform BluetoothAdapter instances that service is up
-                        sendBluetoothServiceUpCallback();
-
-                        //Do enable request
-                        try {
-                            if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
-                                Slog.e(TAG, "IBluetooth.enable() returned false");
-                            }
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "Unable to call enable()", e);
-                        }
-                    } finally {
-                        mBluetoothLock.writeLock().unlock();
-                    }
-
-                    if (!mEnable) {
-                        waitForState(Set.of(BluetoothAdapter.STATE_ON));
-                        handleDisable();
-                        waitForState(Set.of(BluetoothAdapter.STATE_OFF,
-                                BluetoothAdapter.STATE_TURNING_ON,
-                                BluetoothAdapter.STATE_TURNING_OFF,
-                                BluetoothAdapter.STATE_BLE_TURNING_ON,
-                                BluetoothAdapter.STATE_BLE_ON,
-                                BluetoothAdapter.STATE_BLE_TURNING_OFF));
-                    }
-                    break;
-                }
-                case MESSAGE_BLUETOOTH_STATE_CHANGE: {
-                    int prevState = msg.arg1;
-                    int newState = msg.arg2;
-                    if (DBG) {
-                        Slog.d(TAG,
-                                "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(
-                                        prevState) + " > " + BluetoothAdapter.nameForState(
-                                        newState));
-                    }
-                    mState = newState;
-                    bluetoothStateChangeHandler(prevState, newState);
-                    // handle error state transition case from TURNING_ON to OFF
-                    // unbind and rebind bluetooth service and enable bluetooth
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState
-                            == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) {
-                        recoverBluetoothServiceFromError(false);
-                    }
-                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState
-                            == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) {
-                        recoverBluetoothServiceFromError(true);
-                    }
-                    // If we tried to enable BT while BT was in the process of shutting down,
-                    // wait for the BT process to fully tear down and then force a restart
-                    // here.  This is a bit of a hack (b/29363429).
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState
-                            == BluetoothAdapter.STATE_OFF)) {
-                        if (mEnable) {
-                            Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
-                            waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-                            Message restartMsg =
-                                    mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                            mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
-                        }
-                    }
-                    if (newState == BluetoothAdapter.STATE_ON
-                            || newState == BluetoothAdapter.STATE_BLE_ON) {
-                        // bluetooth is working, reset the counter
-                        if (mErrorRecoveryRetryCounter != 0) {
-                            Slog.w(TAG, "bluetooth is recovered from error");
-                            mErrorRecoveryRetryCounter = 0;
-                        }
-                    }
-                    break;
-                }
-                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: {
-                    Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
-                    try {
-                        mBluetoothLock.writeLock().lock();
-                        if (msg.arg1 == SERVICE_IBLUETOOTH) {
-                            // if service is unbinded already, do nothing and return
-                            if (mBluetooth == null) {
-                                break;
-                            }
-                            mBluetooth = null;
-                        } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
-                            mBluetoothGatt = null;
-                            break;
-                        } else {
-                            Slog.e(TAG, "Unknown argument for service disconnect!");
-                            break;
-                        }
-                    } finally {
-                        mBluetoothLock.writeLock().unlock();
-                    }
-
-                    // log the unexpected crash
-                    addCrashLog();
-                    addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH,
-                            mContext.getPackageName(), false);
-                    if (mEnable) {
-                        mEnable = false;
-                        // Send a Bluetooth Restart message
-                        Message restartMsg =
-                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
-                    }
-
-                    sendBluetoothServiceDownCallback();
-
-                    // Send BT state broadcast to update
-                    // the BT icon correctly
-                    if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState
-                            == BluetoothAdapter.STATE_ON)) {
-                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                                BluetoothAdapter.STATE_TURNING_OFF);
-                        mState = BluetoothAdapter.STATE_TURNING_OFF;
-                    }
-                    if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
-                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                                BluetoothAdapter.STATE_OFF);
-                    }
-
-                    mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-                    mState = BluetoothAdapter.STATE_OFF;
-                    break;
-                }
-                case MESSAGE_RESTART_BLUETOOTH_SERVICE: {
-                    mErrorRecoveryRetryCounter++;
-                    Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE: retry count="
-                            + mErrorRecoveryRetryCounter);
-                    if (mErrorRecoveryRetryCounter < MAX_ERROR_RESTART_RETRIES) {
-                        /* Enable without persisting the setting as
-                         it doesnt change when IBluetooth
-                         service restarts */
-                        mEnable = true;
-                        addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED,
-                                mContext.getPackageName(), true);
-                        handleEnable(mQuietEnable);
-                    } else {
-                        Slog.e(TAG, "Reach maximum retry to restart Bluetooth!");
-                    }
-                    break;
-                }
-                case MESSAGE_TIMEOUT_BIND: {
-                    Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
-                    mBluetoothLock.writeLock().lock();
-                    mBinding = false;
-                    mBluetoothLock.writeLock().unlock();
-                    break;
-                }
-                case MESSAGE_TIMEOUT_UNBIND: {
-                    Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
-                    mBluetoothLock.writeLock().lock();
-                    mUnbinding = false;
-                    mBluetoothLock.writeLock().unlock();
-                    break;
-                }
-
-                case MESSAGE_USER_SWITCHED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_USER_SWITCHED");
-                    }
-                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
-
-                    /* disable and enable BT when detect a user switch */
-                    if (mBluetooth != null && isEnabled()) {
-                        restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH);
-                    } else if (mBinding || mBluetooth != null) {
-                        Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
-                        userMsg.arg2 = 1 + msg.arg2;
-                        // if user is switched when service is binding retry after a delay
-                        mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
-                        if (DBG) {
-                            Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2);
-                        }
-                    }
-                    break;
-                }
-                case MESSAGE_USER_UNLOCKED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
-                    }
-                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
-
-                    if (mEnable && !mBinding && (mBluetooth == null)) {
-                        // We should be connected, but we gave up for some
-                        // reason; maybe the Bluetooth service wasn't encryption
-                        // aware, so try binding again.
-                        if (DBG) {
-                            Slog.d(TAG, "Enabled but not bound; retrying after unlock");
-                        }
-                        handleEnable(mQuietEnable);
-                    }
-                    break;
-                }
-                case MESSAGE_INIT_FLAGS_CHANGED: {
-                    if (DBG) {
-                        Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
-                    }
-                    mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
-                    if (mBluetoothModeChangeHelper.isMediaProfileConnected()) {
-                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
-                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
-                                + " ms due to existing connections");
-                        mHandler.sendEmptyMessageDelayed(
-                                MESSAGE_INIT_FLAGS_CHANGED,
-                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
-                        break;
-                    }
-                    if (!isDeviceProvisioned()) {
-                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
-                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
-                                +  "ms because device is not provisioned");
-                        mHandler.sendEmptyMessageDelayed(
-                                MESSAGE_INIT_FLAGS_CHANGED,
-                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
-                        break;
-                    }
-                    if (mBluetooth != null && isEnabled()) {
-                        Slog.i(TAG, "Restarting Bluetooth due to init flag change");
-                        restartForReason(
-                                BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
-                    }
-                    break;
-                }
-            }
-        }
-
-        @RequiresPermission(allOf = {
-                android.Manifest.permission.BLUETOOTH_CONNECT,
-                android.Manifest.permission.BLUETOOTH_PRIVILEGED
-        })
-        private void restartForReason(int reason) {
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) {
-                    mBluetooth.unregisterCallback(mBluetoothCallback,
-                            mContext.getAttributionSource());
-                }
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Unable to unregister", re);
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-
-            if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
-                // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE
-                bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF);
-                mState = BluetoothAdapter.STATE_OFF;
-            }
-            if (mState == BluetoothAdapter.STATE_OFF) {
-                bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON);
-                mState = BluetoothAdapter.STATE_TURNING_ON;
-            }
-
-            waitForState(Set.of(BluetoothAdapter.STATE_ON));
-
-            if (mState == BluetoothAdapter.STATE_TURNING_ON) {
-                bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
-            }
-
-            unbindAllBluetoothProfileServices();
-            // disable
-            addActiveLog(reason, mContext.getPackageName(), false);
-            handleDisable();
-            // Pbap service need receive STATE_TURNING_OFF intent to close
-            bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                    BluetoothAdapter.STATE_TURNING_OFF);
-
-            boolean didDisableTimeout =
-                    !waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-
-            bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                    BluetoothAdapter.STATE_OFF);
-            sendBluetoothServiceDownCallback();
-
-            try {
-                mBluetoothLock.writeLock().lock();
-                if (mBluetooth != null) {
-                    mBluetooth = null;
-                    // Unbind
-                    mContext.unbindService(mConnection);
-                }
-                mBluetoothGatt = null;
-            } finally {
-                mBluetoothLock.writeLock().unlock();
-            }
-
-            //
-            // If disabling Bluetooth times out, wait for an
-            // additional amount of time to ensure the process is
-            // shut down completely before attempting to restart.
-            //
-            if (didDisableTimeout) {
-                SystemClock.sleep(3000);
-            } else {
-                SystemClock.sleep(100);
-            }
-
-            mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-            mState = BluetoothAdapter.STATE_OFF;
-            // enable
-            addActiveLog(reason, mContext.getPackageName(), true);
-            // mEnable flag could have been reset on disableBLE. Reenable it.
-            mEnable = true;
-            handleEnable(mQuietEnable);
-        }
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void handleEnable(boolean quietMode) {
-        mQuietEnable = quietMode;
-
-        try {
-            mBluetoothLock.writeLock().lock();
-            if ((mBluetooth == null) && (!mBinding)) {
-                Slog.d(TAG, "binding Bluetooth service");
-                //Start bind timeout and bind
-                Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
-                mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
-                Intent i = new Intent(IBluetooth.class.getName());
-                if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                        UserHandle.CURRENT)) {
-                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-                } else {
-                    mBinding = true;
-                }
-            } else if (mBluetooth != null) {
-                //Enable bluetooth
-                try {
-                    if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) {
-                        Slog.e(TAG, "IBluetooth.enable() returned false");
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call enable()", e);
-                }
-            }
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-    }
-
-    boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
-        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(comp);
-        if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
-            Slog.e(TAG, "Fail to bind to: " + intent);
-            return false;
-        }
-        return true;
-    }
-
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    private void handleDisable() {
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                if (DBG) {
-                    Slog.d(TAG, "Sending off request.");
-                }
-                if (!mBluetooth.disable(mContext.getAttributionSource())) {
-                    Slog.e(TAG, "IBluetooth.disable() returned false");
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to call disable()", e);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-    }
-
-    private boolean checkIfCallerIsForegroundUser() {
-        int foregroundUser;
-        int callingUser = UserHandle.getCallingUserId();
-        int callingUid = Binder.getCallingUid();
-        final long callingIdentity = Binder.clearCallingIdentity();
-        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        UserInfo ui = um.getProfileParent(callingUser);
-        int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
-        int callingAppId = UserHandle.getAppId(callingUid);
-        boolean valid = false;
-        try {
-            foregroundUser = ActivityManager.getCurrentUser();
-            valid = (callingUser == foregroundUser) || parentUser == foregroundUser
-                    || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid
-                    || callingAppId == Process.SHELL_UID;
-            if (DBG && !valid) {
-                Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser="
-                        + callingUser + " parentUser=" + parentUser + " foregroundUser="
-                        + foregroundUser);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-        return valid;
-    }
-
-    private void sendBleStateChanged(int prevState, int newState) {
-        if (DBG) {
-            Slog.d(TAG,
-                    "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
-                            + BluetoothAdapter.nameForState(newState));
-        }
-        // Send broadcast message to everyone else
-        Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
-        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions());
-    }
-
-    private boolean isBleState(int state) {
-        switch (state) {
-            case BluetoothAdapter.STATE_BLE_ON:
-            case BluetoothAdapter.STATE_BLE_TURNING_ON:
-            case BluetoothAdapter.STATE_BLE_TURNING_OFF:
-                return true;
-        }
-        return false;
-    }
-
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    private void bluetoothStateChangeHandler(int prevState, int newState) {
-        boolean isStandardBroadcast = true;
-        if (prevState == newState) { // No change. Nothing to do.
-            return;
-        }
-        // Notify all proxy objects first of adapter state change
-        if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) {
-            boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
-                    && newState == BluetoothAdapter.STATE_BLE_ON);
-
-            if (newState == BluetoothAdapter.STATE_OFF) {
-                // If Bluetooth is off, send service down event to proxy objects, and unbind
-                if (DBG) {
-                    Slog.d(TAG, "Bluetooth is complete send Service Down");
-                }
-                sendBluetoothServiceDownCallback();
-                unbindAndFinish();
-                sendBleStateChanged(prevState, newState);
-
-                /* Currently, the OFF intent is broadcasted externally only when we transition
-                 * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state,
-                 * we are guaranteed that the OFF intent has been broadcasted earlier and we
-                 * can safely skip it.
-                 * Conversely, if the previous state is not a BLE state, it indicates that some
-                 * sort of crash has occurred, moving us directly to STATE_OFF without ever
-                 * passing through BLE_ON. We should broadcast the OFF intent in this case. */
-                isStandardBroadcast = !isBleState(prevState);
-
-            } else if (!intermediate_off) {
-                // connect to GattService
-                if (DBG) {
-                    Slog.d(TAG, "Bluetooth is in LE only mode");
-                }
-                if (mBluetoothGatt != null || !mContext.getPackageManager()
-                            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
-                    continueFromBleOnState();
-                } else {
-                    if (DBG) {
-                        Slog.d(TAG, "Binding Bluetooth GATT service");
-                    }
-                    Intent i = new Intent(IBluetoothGatt.class.getName());
-                    doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                            UserHandle.CURRENT);
-                }
-                sendBleStateChanged(prevState, newState);
-                //Don't broadcase this as std intent
-                isStandardBroadcast = false;
-
-            } else if (intermediate_off) {
-                if (DBG) {
-                    Slog.d(TAG, "Intermediate off, back to LE only mode");
-                }
-                // For LE only mode, broadcast as is
-                sendBleStateChanged(prevState, newState);
-                sendBluetoothStateCallback(false); // BT is OFF for general users
-                // Broadcast as STATE_OFF
-                newState = BluetoothAdapter.STATE_OFF;
-                sendBrEdrDownCallback(mContext.getAttributionSource());
-            }
-        } else if (newState == BluetoothAdapter.STATE_ON) {
-            boolean isUp = (newState == BluetoothAdapter.STATE_ON);
-            sendBluetoothStateCallback(isUp);
-            sendBleStateChanged(prevState, newState);
-
-        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
-                || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            sendBleStateChanged(prevState, newState);
-            isStandardBroadcast = false;
-
-        } else if (newState == BluetoothAdapter.STATE_TURNING_ON
-                || newState == BluetoothAdapter.STATE_TURNING_OFF) {
-            sendBleStateChanged(prevState, newState);
-        }
-
-        if (isStandardBroadcast) {
-            if (prevState == BluetoothAdapter.STATE_BLE_ON) {
-                // Show prevState of BLE_ON as OFF to standard users
-                prevState = BluetoothAdapter.STATE_OFF;
-            }
-            if (DBG) {
-                Slog.d(TAG,
-                        "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
-                                + BluetoothAdapter.nameForState(newState));
-            }
-            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null,
-                    getTempAllowlistBroadcastOptions());
-        }
-    }
-
-    private boolean waitForState(Set<Integer> states) {
-        int i = 0;
-        while (i < 10) {
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth == null) {
-                    break;
-                }
-                if (states.contains(mBluetooth.getState())) {
-                    return true;
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "getState()", e);
-                break;
-            } finally {
-                mBluetoothLock.readLock().unlock();
-            }
-            SystemClock.sleep(300);
-            i++;
-        }
-        Slog.e(TAG, "waitForState " + states + " time out");
-        return false;
-    }
-
-    private void sendDisableMsg(int reason, String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE));
-        addActiveLog(reason, packageName, false);
-    }
-
-    private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
-        sendEnableMsg(quietMode, reason, packageName, false);
-    }
-
-    private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0,
-                  isBle ? 1 : 0));
-        addActiveLog(reason, packageName, true);
-        mLastEnabledTime = SystemClock.elapsedRealtime();
-    }
-
-    private void addActiveLog(int reason, String packageName, boolean enable) {
-        synchronized (mActiveLogs) {
-            if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) {
-                mActiveLogs.remove();
-            }
-            mActiveLogs.add(
-                    new ActiveLog(reason, packageName, enable, System.currentTimeMillis()));
-        }
-
-        int state = enable ? FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED :
-                             FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
-        FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
-                Binder.getCallingUid(), null, state, reason, packageName);
-    }
-
-    private void addCrashLog() {
-        synchronized (mCrashTimestamps) {
-            if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) {
-                mCrashTimestamps.removeFirst();
-            }
-            mCrashTimestamps.add(System.currentTimeMillis());
-            mCrashes++;
-        }
-    }
-
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    private void recoverBluetoothServiceFromError(boolean clearBle) {
-        Slog.e(TAG, "recoverBluetoothServiceFromError");
-        try {
-            mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) {
-                //Unregister callback object
-                mBluetooth.unregisterCallback(mBluetoothCallback, mContext.getAttributionSource());
-            }
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to unregister", re);
-        } finally {
-            mBluetoothLock.readLock().unlock();
-        }
-
-        SystemClock.sleep(500);
-
-        // disable
-        addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR,
-                mContext.getPackageName(), false);
-        handleDisable();
-
-        waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-
-        sendBluetoothServiceDownCallback();
-
-        try {
-            mBluetoothLock.writeLock().lock();
-            if (mBluetooth != null) {
-                mBluetooth = null;
-                // Unbind
-                mContext.unbindService(mConnection);
-            }
-            mBluetoothGatt = null;
-        } finally {
-            mBluetoothLock.writeLock().unlock();
-        }
-
-        mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
-        mState = BluetoothAdapter.STATE_OFF;
-
-        if (clearBle) {
-            clearBleApps();
-        }
-
-        mEnable = false;
-
-        // Send a Bluetooth Restart message to reenable bluetooth
-        Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-        mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS);
-    }
-
-    private boolean isBluetoothDisallowed() {
-        final long callingIdentity = Binder.clearCallingIdentity();
-        try {
-            return mContext.getSystemService(UserManager.class)
-                    .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM);
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-    }
-
-    /**
-     * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not
-     * offered to the user if Bluetooth or sharing is disallowed. Puts the component to its default
-     * state if Bluetooth is not disallowed.
-     *
-     * @param userId user to disable bluetooth sharing for.
-     * @param bluetoothSharingDisallowed whether bluetooth sharing is disallowed.
-     */
-    private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) {
-        final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
-                "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
-        final int newState =
-                bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-        try {
-            final IPackageManager imp = AppGlobals.getPackageManager();
-            imp.setComponentEnabledSetting(oppLauncherComponent, newState,
-                    PackageManager.DONT_KILL_APP, userId);
-        } catch (Exception e) {
-            // The component was not found, do nothing.
-        }
-    }
-
-    private int getServiceRestartMs() {
-        return (mErrorRecoveryRetryCounter + 1) * SERVICE_RESTART_TIME_MS;
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
-            return;
-        }
-        if ((args.length > 0) && args[0].startsWith("--proto")) {
-            dumpProto(fd);
-            return;
-        }
-        String errorMsg = null;
-
-        writer.println("Bluetooth Status");
-        writer.println("  enabled: " + isEnabled());
-        writer.println("  state: " + BluetoothAdapter.nameForState(mState));
-        writer.println("  address: " + mAddress);
-        writer.println("  name: " + mName);
-        if (mEnable) {
-            long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
-            String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
-                    (int) (onDuration / (1000 * 60 * 60)),
-                    (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
-                    (int) (onDuration % 1000));
-            writer.println("  time since enabled: " + onDurationString);
-        }
-
-        if (mActiveLogs.size() == 0) {
-            writer.println("\nBluetooth never enabled!");
-        } else {
-            writer.println("\nEnable log:");
-            for (ActiveLog log : mActiveLogs) {
-                writer.println("  " + log);
-            }
-        }
-
-        writer.println(
-                "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
-        if (mCrashes == CRASH_LOG_MAX_SIZE) {
-            writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
-        }
-        for (Long time : mCrashTimestamps) {
-            writer.println("  " + timeToLog(time));
-        }
-
-        writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
-                + " registered");
-        for (ClientDeathRecipient app : mBleApps.values()) {
-            writer.println("  " + app.getPackageName());
-        }
-
-        writer.println("\nBluetoothManagerService:");
-        writer.println("  mEnable:" + mEnable);
-        writer.println("  mQuietEnable:" + mQuietEnable);
-        writer.println("  mEnableExternal:" + mEnableExternal);
-        writer.println("  mQuietEnableExternal:" + mQuietEnableExternal);
-
-        writer.println("");
-        writer.flush();
-        if (args.length == 0) {
-            // Add arg to produce output
-            args = new String[1];
-            args[0] = "--print";
-        }
-
-        if (mBluetoothBinder == null) {
-            errorMsg = "Bluetooth Service not connected";
-        } else {
-            try {
-                mBluetoothBinder.dump(fd, args);
-            } catch (RemoteException re) {
-                errorMsg = "RemoteException while dumping Bluetooth Service";
-            }
-        }
-        if (errorMsg != null) {
-            writer.println(errorMsg);
-        }
-    }
-
-    private void dumpProto(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
-        proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled());
-        proto.write(BluetoothManagerServiceDumpProto.STATE, mState);
-        proto.write(BluetoothManagerServiceDumpProto.STATE_NAME,
-                BluetoothAdapter.nameForState(mState));
-        proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress);
-        proto.write(BluetoothManagerServiceDumpProto.NAME, mName);
-        if (mEnable) {
-            proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime);
-        }
-        proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS,
-                SystemClock.elapsedRealtime());
-        for (ActiveLog log : mActiveLogs) {
-            long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS);
-            log.dump(proto);
-            proto.end(token);
-        }
-        proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes);
-        proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED,
-                mCrashes == CRASH_LOG_MAX_SIZE);
-        for (Long time : mCrashTimestamps) {
-            proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time);
-        }
-        proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size());
-        for (ClientDeathRecipient app : mBleApps.values()) {
-            proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES,
-                    app.getPackageName());
-        }
-        proto.flush();
-    }
-
-    private static String getEnableDisableReasonString(int reason) {
-        switch (reason) {
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST:
-                return "APPLICATION_REQUEST";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE:
-                return "AIRPLANE_MODE";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED:
-                return "DISALLOWED";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED:
-                return "RESTARTED";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR:
-                return "START_ERROR";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT:
-                return "SYSTEM_BOOT";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH:
-                return "CRASH";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH:
-                return "USER_SWITCH";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING:
-                return "RESTORE_USER_SETTING";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET:
-                return "FACTORY_RESET";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED:
-                return "INIT_FLAGS_CHANGED";
-            case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED:
-            default: return "UNKNOWN[" + reason + "]";
-        }
-    }
-
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    private static boolean checkPermissionForDataDelivery(Context context, String permission,
-            AttributionSource attributionSource, String message) {
-        PermissionManager pm = context.getSystemService(PermissionManager.class);
-        if (pm == null) {
-            return false;
-        }
-        AttributionSource currentAttribution = new AttributionSource
-                .Builder(context.getAttributionSource())
-                .setNext(attributionSource)
-                .build();
-        final int result = pm.checkPermissionForDataDeliveryFromDataSource(permission,
-                currentAttribution, message);
-        if (result == PERMISSION_GRANTED) {
-            return true;
-        }
-
-        final String msg = "Need " + permission + " permission for " + attributionSource + ": "
-                + message;
-        if (result == PERMISSION_HARD_DENIED) {
-            throw new SecurityException(msg);
-        } else {
-            Log.w(TAG, msg);
-            return false;
-        }
-    }
-
-    /**
-     * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns
-     * false if the result is a soft denial. Throws SecurityException if the result is a hard
-     * denial.
-     *
-     * <p>Should be used in situations where the app op should not be noted.
-     */
-    @SuppressLint("AndroidFrameworkRequiresPermission")
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
-    public static boolean checkConnectPermissionForDataDelivery(
-            Context context, AttributionSource attributionSource, String message) {
-        return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT,
-                attributionSource, message);
-    }
-
-    static @NonNull Bundle getTempAllowlistBroadcastOptions() {
-        final long duration = 10_000;
-        final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
-        bOptions.setTemporaryAppAllowlist(duration,
-                TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
-                PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, "");
-        return bOptions.toBundle();
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
deleted file mode 100644
index e5854c9..0000000
--- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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 com.android.server;
-
-import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
-import android.content.Context;
-import android.content.res.Resources;
-import android.provider.Settings;
-import android.widget.Toast;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Helper class that handles callout and callback methods without
- * complex logic.
- */
-public class BluetoothModeChangeHelper {
-    private volatile BluetoothA2dp mA2dp;
-    private volatile BluetoothHearingAid mHearingAid;
-    private volatile BluetoothLeAudio mLeAudio;
-    private final BluetoothAdapter mAdapter;
-    private final Context mContext;
-
-    BluetoothModeChangeHelper(Context context) {
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mContext = context;
-
-        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
-        mAdapter.getProfileProxy(mContext, mProfileServiceListener,
-                BluetoothProfile.HEARING_AID);
-        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO);
-    }
-
-    private final ServiceListener mProfileServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            // Setup Bluetooth profile proxies
-            switch (profile) {
-                case BluetoothProfile.A2DP:
-                    mA2dp = (BluetoothA2dp) proxy;
-                    break;
-                case BluetoothProfile.HEARING_AID:
-                    mHearingAid = (BluetoothHearingAid) proxy;
-                    break;
-                case BluetoothProfile.LE_AUDIO:
-                    mLeAudio = (BluetoothLeAudio) proxy;
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(int profile) {
-            // Clear Bluetooth profile proxies
-            switch (profile) {
-                case BluetoothProfile.A2DP:
-                    mA2dp = null;
-                    break;
-                case BluetoothProfile.HEARING_AID:
-                    mHearingAid = null;
-                    break;
-                case BluetoothProfile.LE_AUDIO:
-                    mLeAudio = null;
-                    break;
-                default:
-                    break;
-            }
-        }
-    };
-
-    @VisibleForTesting
-    public boolean isMediaProfileConnected() {
-        return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected();
-    }
-
-    @VisibleForTesting
-    public boolean isBluetoothOn() {
-        final BluetoothAdapter adapter = mAdapter;
-        if (adapter == null) {
-            return false;
-        }
-        return adapter.getLeState() == BluetoothAdapter.STATE_ON;
-    }
-
-    @VisibleForTesting
-    public boolean isAirplaneModeOn() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
-    @VisibleForTesting
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void onAirplaneModeChanged(BluetoothManagerService managerService) {
-        managerService.onAirplaneModeChanged();
-    }
-
-    @VisibleForTesting
-    public int getSettingsInt(String name) {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                name, 0);
-    }
-
-    @VisibleForTesting
-    public void setSettingsInt(String name, int value) {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                name, value);
-    }
-
-    @VisibleForTesting
-    public void showToastMessage() {
-        Resources r = mContext.getResources();
-        final CharSequence text = r.getString(
-                R.string.bluetooth_airplane_mode_toast, 0);
-        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
-    }
-
-    private boolean isA2dpConnected() {
-        final BluetoothA2dp a2dp = mA2dp;
-        if (a2dp == null) {
-            return false;
-        }
-        return a2dp.getConnectedDevices().size() > 0;
-    }
-
-    private boolean isHearingAidConnected() {
-        final BluetoothHearingAid hearingAid = mHearingAid;
-        if (hearingAid == null) {
-            return false;
-        }
-        return hearingAid.getConnectedDevices().size() > 0;
-    }
-
-    private boolean isLeAudioConnected() {
-        final BluetoothLeAudio leAudio = mLeAudio;
-        if (leAudio == null) {
-            return false;
-        }
-        return leAudio.getConnectedDevices().size() > 0;
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
deleted file mode 100644
index 1a1eecd..0000000
--- a/services/core/java/com/android/server/BluetoothService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.os.UserManager;
-
-import com.android.server.SystemService.TargetUser;
-
-class BluetoothService extends SystemService {
-    private BluetoothManagerService mBluetoothManagerService;
-    private boolean mInitialized = false;
-
-    public BluetoothService(Context context) {
-        super(context);
-        mBluetoothManagerService = new BluetoothManagerService(context);
-    }
-
-    private void initialize() {
-        if (!mInitialized) {
-            mBluetoothManagerService.handleOnBootPhase();
-            mInitialized = true;
-        }
-    }
-
-    @Override
-    public void onStart() {
-    }
-
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,
-                    mBluetoothManagerService);
-        } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY &&
-                !UserManager.isHeadlessSystemUserMode()) {
-            initialize();
-        }
-    }
-
-    @Override
-    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
-        if (!mInitialized) {
-            initialize();
-        } else {
-            mBluetoothManagerService.handleOnSwitchUser(to.getUserIdentifier());
-        }
-    }
-
-    @Override
-    public void onUserUnlocking(@NonNull TargetUser user) {
-        mBluetoothManagerService.handleOnUnlockUser(user.getUserIdentifier());
-    }
-}
diff --git a/services/core/java/com/android/server/Dumpable.java b/services/core/java/com/android/server/Dumpable.java
index 866f81c..004f923 100644
--- a/services/core/java/com/android/server/Dumpable.java
+++ b/services/core/java/com/android/server/Dumpable.java
@@ -24,6 +24,8 @@
  *
  * <p>See {@link SystemServer.SystemServerDumper} for usage example.
  */
+// TODO(b/149254050): replace / merge with package android.util.Dumpable (it would require
+// exporting IndentingPrintWriter as @SystemApi) and/or changing the method to use a prefix
 public interface Dumpable {
 
     /**
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 513d86e7..62dc2733 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -126,8 +126,8 @@
 
     private boolean wipeUser(Context context, @UserIdInt int userId, String wipeReason) {
         final UserManager userManager = context.getSystemService(UserManager.class);
-        final int result = userManager.removeUserOrSetEphemeral(
-                userId, /* evenWhenDisallowed= */ false);
+        final int result = userManager.removeUserWhenPossible(
+                UserHandle.of(userId), /* overrideDevicePolicy= */ false);
         if (result == UserManager.REMOVE_RESULT_ERROR) {
             Slogf.e(TAG, "Can't remove user %d", userId);
             return false;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7993936..b92556b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3472,11 +3472,9 @@
     }
 
     private int getAllowMode(Intent service, @Nullable String callingPackage) {
-        if (callingPackage == null || service.getComponent() == null) {
-            return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
-        }
-        if (callingPackage.equals(service.getComponent().getPackageName())) {
-            return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
+        if (callingPackage != null && service.getComponent() != null
+                && callingPackage.equals(service.getComponent().getPackageName())) {
+            return ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
         } else {
             return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 70bd734..e933526 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -393,6 +393,7 @@
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.os.NativeTombstoneManager;
 import com.android.server.pm.Installer;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.uri.GrantUri;
@@ -13312,6 +13313,14 @@
             }
 
             switch (action) {
+                case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
+                    UserManagerInternal umInternal = LocalServices.getService(
+                            UserManagerInternal.class);
+                    UserInfo userInfo = umInternal.getUserInfo(userId);
+                    if (userInfo != null && userInfo.isCloneProfile()) {
+                        userId = umInternal.getProfileParentId(userId);
+                    }
+                    break;
                 case Intent.ACTION_UID_REMOVED:
                 case Intent.ACTION_PACKAGE_REMOVED:
                 case Intent.ACTION_PACKAGE_CHANGED:
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a7864b9..028a0ec 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -26,10 +26,10 @@
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
 import static android.os.PowerWhitelistManager.REASON_BOOT_COMPLETED;
 import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -2153,11 +2153,10 @@
                     callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
                 // If the caller does not have either permission, they are always doomed.
                 allow = false;
-            } else if (allowMode == ALLOW_NON_FULL) {
+            } else if (allowMode == ALLOW_NON_FULL || allowMode == ALLOW_PROFILES_OR_NON_FULL) {
                 // We are blanket allowing non-full access, you lucky caller!
                 allow = true;
-            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
-                        || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
                 // We may or may not allow this depending on whether the two users are
                 // in the same profile.
                 allow = isSameProfileGroup;
@@ -2184,12 +2183,13 @@
                     builder.append("; this requires ");
                     builder.append(INTERACT_ACROSS_USERS_FULL);
                     if (allowMode != ALLOW_FULL_ONLY) {
-                        if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) {
+                        if (allowMode == ALLOW_NON_FULL
+                                || allowMode == ALLOW_PROFILES_OR_NON_FULL
+                                || (allowMode == ALLOW_NON_FULL_IN_PROFILE && isSameProfileGroup)) {
                             builder.append(" or ");
                             builder.append(INTERACT_ACROSS_USERS);
                         }
-                        if (isSameProfileGroup
-                                && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+                        if (isSameProfileGroup && allowMode == ALLOW_PROFILES_OR_NON_FULL) {
                             builder.append(" or ");
                             builder.append(INTERACT_ACROSS_PROFILES);
                         }
@@ -2216,19 +2216,14 @@
     private boolean canInteractWithAcrossProfilesPermission(
             int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
             String callingPackage) {
-        if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+        if (allowMode != ALLOW_PROFILES_OR_NON_FULL) {
             return false;
         }
         if (!isSameProfileGroup) {
             return false;
         }
-        return  PermissionChecker.PERMISSION_GRANTED
-                == PermissionChecker.checkPermissionForPreflight(
-                        mInjector.getContext(),
-                        INTERACT_ACROSS_PROFILES,
-                        callingPid,
-                        callingUid,
-                        callingPackage);
+        return mInjector.checkPermissionForPreflight(INTERACT_ACROSS_PROFILES, callingPid,
+                callingUid, callingPackage);
     }
 
     int unsafeConvertIncomingUser(@UserIdInt int userId) {
@@ -3157,6 +3152,12 @@
             return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
         }
 
+        boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
+            return  PermissionChecker.PERMISSION_GRANTED
+                    == PermissionChecker.checkPermissionForPreflight(
+                            getContext(), permission, pid, uid, pkg);
+        }
+
         protected void startHomeActivity(@UserIdInt int userId, String reason) {
             mService.mAtmInternal.startHomeActivity(userId, reason);
         }
@@ -3234,7 +3235,7 @@
             mService.mAtmInternal.clearLockedTasks(reason);
         }
 
-        protected boolean isCallerRecents(int callingUid) {
+        boolean isCallerRecents(int callingUid) {
             return mService.mAtmInternal.isCallerRecents(callingUid);
         }
 
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2fcdd61..565e295 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1219,6 +1219,7 @@
                         mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj,
                                 mAudioService.getBluetoothContextualVolumeStream());
                     }
+                    break;
                 case MSG_BT_HEADSET_CNCT_FAILED:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 023a11e..0961fcb3 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1124,6 +1124,12 @@
     private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
             String eventSource) {
         if (device != AudioSystem.DEVICE_NONE) {
+
+            /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
+             * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
+             */
+            mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
             AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
                     address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 805e45d..57ae36ed 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2611,6 +2611,16 @@
         return getDevicesForAttributesInt(attributes);
     }
 
+    /** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
+     * This method is similar with AudioService#getDevicesForAttributes,
+     * only it doesn't enforce permissions because it is used by an unprivileged public API
+     * instead of the system API.
+     */
+    public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
+            @NonNull AudioAttributes attributes) {
+        return getDevicesForAttributesInt(attributes);
+    }
+
     /**
      * @see AudioManager#isMusicActive()
      * @param remotely true if query is for remote playback (cast), false for local playback.
@@ -9398,6 +9408,7 @@
         pw.println("mHasSpatializerEffect:" + mHasSpatializerEffect);
         pw.println("isSpatializerEnabled:" + isSpatializerEnabled());
         pw.println("isSpatialAudioEnabled:" + isSpatialAudioEnabled());
+        mSpatializerHelper.dump(pw);
 
         mAudioSystem.dump(pw);
     }
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 406b2dd2..74c8999 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -252,6 +252,9 @@
             AudioAttributes.FLAG_BYPASS_MUTE;
 
     private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
+        if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID) {
+            return;
+        }
         if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
                 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
             if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index b47ea4f..e6789d5 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -84,7 +85,7 @@
     private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
-    private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+    private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
     private int mSpatOutput = 0;
     private @Nullable ISpatializer mSpat;
     private @Nullable SpatializerCallback mSpatCallback;
@@ -681,12 +682,13 @@
             return;
         }
         try {
-            if (mode != mDesiredHeadTrackingMode) {
-                mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode));
+            if (mDesiredHeadTrackingMode != mode) {
                 mDesiredHeadTrackingMode = mode;
                 dispatchDesiredHeadTrackingMode(mode);
             }
-
+            if (mode != headTrackingModeTypeToSpatializerInt(mSpat.getActualHeadTrackingMode())) {
+                mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode));
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling setDesiredHeadTrackingMode", e);
         }
@@ -937,6 +939,7 @@
         } catch (Exception e) {
             Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e);
         }
+        setDesiredHeadTrackingMode(mDesiredHeadTrackingMode);
     }
 
     //------------------------------------------------------
@@ -983,4 +986,22 @@
                 throw(new IllegalArgumentException("Unexpected spatializer level:" + level));
         }
     }
+
+    void dump(PrintWriter pw) {
+        pw.println("SpatializerHelper:");
+        pw.println("\tmState:" + mState);
+        pw.println("\tmSpatLevel:" + mSpatLevel);
+        pw.println("\tmCapableSpatLevel:" + mCapableSpatLevel);
+        pw.println("\tmActualHeadTrackingMode:"
+                + Spatializer.headtrackingModeToString(mActualHeadTrackingMode));
+        pw.println("\tmDesiredHeadTrackingMode:"
+                + Spatializer.headtrackingModeToString(mDesiredHeadTrackingMode));
+        String modesString = "";
+        int[] modes = getSupportedHeadTrackingModes();
+        for (int mode : modes) {
+            modesString += Spatializer.headtrackingModeToString(mode) + " ";
+        }
+        pw.println("\tsupported head tracking modes:" + modesString);
+        pw.println("\tmSpatOutput:" + mSpatOutput);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index b73e911..26bbb40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -48,7 +50,6 @@
      * Interface that ClientMonitor holders should use to receive callbacks.
      */
     public interface Callback {
-
         /**
          * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
          * the queue and becomes the current operation).
@@ -203,7 +204,8 @@
     }
 
     /** Signals this operation has completed its lifecycle and should no longer be used. */
-    void destroy() {
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public void destroy() {
         mAlreadyDone = true;
         if (mToken != null) {
             try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index a358bc2..39c5944 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -17,10 +17,10 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.IntDef;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Handler;
@@ -55,6 +55,7 @@
  * We currently assume (and require) that each biometric sensor have its own instance of a
  * {@link BiometricScheduler}. See {@link CoexCoordinator}.
  */
+@MainThread
 public class BiometricScheduler {
 
     private static final String BASE_TAG = "BiometricScheduler";
@@ -110,123 +111,6 @@
         }
     }
 
-    /**
-     * Contains all the necessary information for a HAL operation.
-     */
-    @VisibleForTesting
-    static final class Operation {
-
-        /**
-         * The operation is added to the list of pending operations and waiting for its turn.
-         */
-        static final int STATE_WAITING_IN_QUEUE = 0;
-
-        /**
-         * The operation is added to the list of pending operations, but a subsequent operation
-         * has been added. This state only applies to {@link Interruptable} operations. When this
-         * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
-         */
-        static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
-
-        /**
-         * The operation has reached the front of the queue and has started.
-         */
-        static final int STATE_STARTED = 2;
-
-        /**
-         * The operation was started, but is now canceling. Operations should wait for the HAL to
-         * acknowledge that the operation was canceled, at which point it finishes.
-         */
-        static final int STATE_STARTED_CANCELING = 3;
-
-        /**
-         * The operation has reached the head of the queue but is waiting for BiometricService
-         * to acknowledge and start the operation.
-         */
-        static final int STATE_WAITING_FOR_COOKIE = 4;
-
-        /**
-         * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
-         */
-        static final int STATE_FINISHED = 5;
-
-        @IntDef({STATE_WAITING_IN_QUEUE,
-                STATE_WAITING_IN_QUEUE_CANCELING,
-                STATE_STARTED,
-                STATE_STARTED_CANCELING,
-                STATE_WAITING_FOR_COOKIE,
-                STATE_FINISHED})
-        @Retention(RetentionPolicy.SOURCE)
-        @interface OperationState {}
-
-        @NonNull final BaseClientMonitor mClientMonitor;
-        @Nullable final BaseClientMonitor.Callback mClientCallback;
-        @OperationState int mState;
-
-        Operation(
-                @NonNull BaseClientMonitor clientMonitor,
-                @Nullable BaseClientMonitor.Callback callback
-        ) {
-            this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
-        }
-
-        protected Operation(
-                @NonNull BaseClientMonitor clientMonitor,
-                @Nullable BaseClientMonitor.Callback callback,
-                @OperationState int state
-        ) {
-            mClientMonitor = clientMonitor;
-            mClientCallback = callback;
-            mState = state;
-        }
-
-        public boolean isHalOperation() {
-            return mClientMonitor instanceof HalClientMonitor<?>;
-        }
-
-        /**
-         * @return true if the operation requires the HAL, and the HAL is null.
-         */
-        public boolean isUnstartableHalOperation() {
-            if (isHalOperation()) {
-                final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
-                if (client.getFreshDaemon() == null) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            return mClientMonitor + ", State: " + mState;
-        }
-    }
-
-    /**
-     * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
-     * kill the current operation and forcibly start the next.
-     */
-    private static final class CancellationWatchdog implements Runnable {
-        static final int DELAY_MS = 3000;
-
-        final String tag;
-        final Operation operation;
-        CancellationWatchdog(String tag, Operation operation) {
-            this.tag = tag;
-            this.operation = operation;
-        }
-
-        @Override
-        public void run() {
-            if (operation.mState != Operation.STATE_FINISHED) {
-                Slog.e(tag, "[Watchdog Triggered]: " + operation);
-                operation.mClientMonitor.mCallback
-                        .onClientFinished(operation.mClientMonitor, false /* success */);
-            }
-        }
-    }
-
     private static final class CrashState {
         static final int NUM_ENTRIES = 10;
         final String timestamp;
@@ -263,10 +147,9 @@
     private final @SensorType int mSensorType;
     @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
     @NonNull private final IBiometricService mBiometricService;
-    @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
-    @NonNull private final InternalCallback mInternalCallback;
-    @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations;
-    @VisibleForTesting @Nullable Operation mCurrentOperation;
+    @NonNull protected final Handler mHandler;
+    @VisibleForTesting @NonNull final Deque<BiometricSchedulerOperation> mPendingOperations;
+    @VisibleForTesting @Nullable BiometricSchedulerOperation mCurrentOperation;
     @NonNull private final ArrayDeque<CrashState> mCrashStates;
 
     private int mTotalOperationsHandled;
@@ -277,7 +160,7 @@
     // Internal callback, notified when an operation is complete. Notifies the requester
     // that the operation is complete, before performing internal scheduler work (such as
     // starting the next client).
-    public class InternalCallback implements BaseClientMonitor.Callback {
+    private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() {
         @Override
         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
             Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -286,16 +169,11 @@
                 mCoexCoordinator.addAuthenticationClient(mSensorType,
                         (AuthenticationClient<?>) clientMonitor);
             }
-
-            if (mCurrentOperation.mClientCallback != null) {
-                mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
-            }
         }
 
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             mHandler.post(() -> {
-                clientMonitor.destroy();
                 if (mCurrentOperation == null) {
                     Slog.e(getTag(), "[Finishing] " + clientMonitor
                             + " but current operation is null, success: " + success
@@ -303,9 +181,9 @@
                     return;
                 }
 
-                if (clientMonitor != mCurrentOperation.mClientMonitor) {
+                if (!mCurrentOperation.isFor(clientMonitor)) {
                     Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
-                            + " current: " + mCurrentOperation.mClientMonitor);
+                            + " current: " + mCurrentOperation);
                     return;
                 }
 
@@ -315,36 +193,33 @@
                             (AuthenticationClient<?>) clientMonitor);
                 }
 
-                mCurrentOperation.mState = Operation.STATE_FINISHED;
-
-                if (mCurrentOperation.mClientCallback != null) {
-                    mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
-                }
-
                 if (mGestureAvailabilityDispatcher != null) {
                     mGestureAvailabilityDispatcher.markSensorActive(
-                            mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
+                            mCurrentOperation.getSensorId(), false /* active */);
                 }
 
                 if (mRecentOperations.size() >= mRecentOperationsLimit) {
                     mRecentOperations.remove(0);
                 }
-                mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
+                mRecentOperations.add(mCurrentOperation.getProtoEnum());
                 mCurrentOperation = null;
                 mTotalOperationsHandled++;
                 startNextOperationIfIdle();
             });
         }
-    }
+    };
 
     @VisibleForTesting
-    BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+    BiometricScheduler(@NonNull String tag,
+            @NonNull Handler handler,
+            @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
-            @NonNull IBiometricService biometricService, int recentOperationsLimit,
+            @NonNull IBiometricService biometricService,
+            int recentOperationsLimit,
             @NonNull CoexCoordinator coexCoordinator) {
         mBiometricTag = tag;
+        mHandler = handler;
         mSensorType = sensorType;
-        mInternalCallback = new InternalCallback();
         mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
         mPendingOperations = new ArrayDeque<>();
         mBiometricService = biometricService;
@@ -356,6 +231,7 @@
 
     /**
      * Creates a new scheduler.
+     *
      * @param tag for the specific instance of the scheduler. Should be unique.
      * @param sensorType the sensorType that this scheduler is handling.
      * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
@@ -364,16 +240,14 @@
     public BiometricScheduler(@NonNull String tag,
             @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-        this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
-                CoexCoordinator.getInstance());
+        this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+                IBiometricService.Stub.asInterface(
+                        ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+                LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance());
     }
 
-    /**
-     * @return A reference to the internal callback that should be invoked whenever the scheduler
-     *         needs to (e.g. client started, client finished).
-     */
-    @NonNull protected InternalCallback getInternalCallback() {
+    @VisibleForTesting
+    public BaseClientMonitor.Callback getInternalCallback() {
         return mInternalCallback;
     }
 
@@ -392,72 +266,46 @@
         }
 
         mCurrentOperation = mPendingOperations.poll();
-        final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;
         Slog.d(getTag(), "[Polled] " + mCurrentOperation);
 
         // If the operation at the front of the queue has been marked for cancellation, send
         // ERROR_CANCELED. No need to start this client.
-        if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+        if (mCurrentOperation.isMarkedCanceling()) {
             Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
-            if (!(currentClient instanceof Interruptable)) {
-                throw new IllegalStateException("Mis-implemented client or scheduler, "
-                        + "trying to cancel non-interruptable operation: " + mCurrentOperation);
-            }
-
-            final Interruptable interruptable = (Interruptable) currentClient;
-            interruptable.cancelWithoutStarting(getInternalCallback());
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
             // Now we wait for the client to send its FinishCallback, which kicks off the next
             // operation.
             return;
         }
 
-        if (mGestureAvailabilityDispatcher != null
-                && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
+        if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) {
             mGestureAvailabilityDispatcher.markSensorActive(
-                    mCurrentOperation.mClientMonitor.getSensorId(),
-                    true /* active */);
+                    mCurrentOperation.getSensorId(), true /* active */);
         }
 
         // Not all operations start immediately. BiometricPrompt waits for its operation
         // to arrive at the head of the queue, before pinging it to start.
-        final boolean shouldStartNow = currentClient.getCookie() == 0;
-        if (shouldStartNow) {
-            if (mCurrentOperation.isUnstartableHalOperation()) {
-                final HalClientMonitor<?> halClientMonitor =
-                        (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
+        final int cookie = mCurrentOperation.isReadyToStart();
+        if (cookie == 0) {
+            if (!mCurrentOperation.start(mInternalCallback)) {
                 // Note down current length of queue
                 final int pendingOperationsLength = mPendingOperations.size();
-                final Operation lastOperation = mPendingOperations.peekLast();
+                final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast();
                 Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
                         + ". Last pending operation: " + lastOperation);
 
-                // For current operations, 1) unableToStart, which notifies the caller-side, then
-                // 2) notify operation's callback, to notify applicable system service that the
-                // operation failed.
-                halClientMonitor.unableToStart();
-                if (mCurrentOperation.mClientCallback != null) {
-                    mCurrentOperation.mClientCallback.onClientFinished(
-                            mCurrentOperation.mClientMonitor, false /* success */);
-                }
-
                 // Then for each operation currently in the pending queue at the time of this
                 // failure, do the same as above. Otherwise, it's possible that something like
                 // setActiveUser fails, but then authenticate (for the wrong user) is invoked.
                 for (int i = 0; i < pendingOperationsLength; i++) {
-                    final Operation operation = mPendingOperations.pollFirst();
-                    if (operation == null) {
+                    final BiometricSchedulerOperation operation = mPendingOperations.pollFirst();
+                    if (operation != null) {
+                        Slog.w(getTag(), "[Aborting Operation] " + operation);
+                        operation.abort();
+                    } else {
                         Slog.e(getTag(), "Null operation, index: " + i
                                 + ", expected length: " + pendingOperationsLength);
-                        break;
                     }
-                    if (operation.isHalOperation()) {
-                        ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
-                    }
-                    if (operation.mClientCallback != null) {
-                        operation.mClientCallback.onClientFinished(operation.mClientMonitor,
-                                false /* success */);
-                    }
-                    Slog.w(getTag(), "[Aborted Operation] " + operation);
                 }
 
                 // It's possible that during cleanup a new set of operations came in. We can try to
@@ -465,25 +313,20 @@
                 // actually be multiple operations (i.e. updateActiveUser + authenticate).
                 mCurrentOperation = null;
                 startNextOperationIfIdle();
-            } else {
-                Slog.d(getTag(), "[Starting] " + mCurrentOperation);
-                currentClient.start(getInternalCallback());
-                mCurrentOperation.mState = Operation.STATE_STARTED;
             }
         } else {
             try {
-                mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+                mBiometricService.onReadyForAuthentication(cookie);
             } catch (RemoteException e) {
                 Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
             }
             Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
-            mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
         }
     }
 
     /**
      * Starts the {@link #mCurrentOperation} if
-     * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+     * 1) its state is {@link BiometricSchedulerOperation#STATE_WAITING_FOR_COOKIE} and
      * 2) its cookie matches this cookie
      *
      * This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
@@ -499,45 +342,13 @@
             Slog.e(getTag(), "Current operation is null");
             return;
         }
-        if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) {
-            if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
-                Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
-                        + mCurrentOperation);
-                // This should trigger the internal onClientFinished callback, which clears the
-                // operation and starts the next one.
-                final ErrorConsumer errorConsumer =
-                        (ErrorConsumer) mCurrentOperation.mClientMonitor;
-                errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
-                        0 /* vendorCode */);
-                return;
-            } else {
-                Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
-                        + ", expected STATE_WAITING_FOR_COOKIE");
-                return;
-            }
-        }
-        if (mCurrentOperation.mClientMonitor.getCookie() != cookie) {
-            Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
-                    + ", received: " + cookie);
-            return;
-        }
 
-        if (mCurrentOperation.isUnstartableHalOperation()) {
+        if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) {
+            Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation);
+        } else {
             Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation);
-            // This is BiometricPrompt trying to auth but something's wrong with the HAL.
-            final HalClientMonitor<?> halClientMonitor =
-                    (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
-            halClientMonitor.unableToStart();
-            if (mCurrentOperation.mClientCallback != null) {
-                mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor,
-                        false /* success */);
-            }
             mCurrentOperation = null;
             startNextOperationIfIdle();
-        } else {
-            Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
-            mCurrentOperation.mState = Operation.STATE_STARTED;
-            mCurrentOperation.mClientMonitor.start(getInternalCallback());
         }
     }
 
@@ -562,17 +373,14 @@
         // pending clients as canceling. Once they reach the head of the queue, the scheduler will
         // send ERROR_CANCELED and skip the operation.
         if (clientMonitor.interruptsPrecedingClients()) {
-            for (Operation operation : mPendingOperations) {
-                if (operation.mClientMonitor instanceof Interruptable
-                        && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
-                    Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
-                            + operation.mClientMonitor);
-                    operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+            for (BiometricSchedulerOperation operation : mPendingOperations) {
+                if (operation.markCanceling()) {
+                    Slog.d(getTag(), "New client, marking pending op as canceling: " + operation);
                 }
             }
         }
 
-        mPendingOperations.add(new Operation(clientMonitor, clientCallback));
+        mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback));
         Slog.d(getTag(), "[Added] " + clientMonitor
                 + ", new queue size: " + mPendingOperations.size());
 
@@ -580,67 +388,34 @@
         // cancellable, start the cancellation process.
         if (clientMonitor.interruptsPrecedingClients()
                 && mCurrentOperation != null
-                && mCurrentOperation.mClientMonitor instanceof Interruptable
-                && mCurrentOperation.mState == Operation.STATE_STARTED) {
+                && mCurrentOperation.isInterruptable()
+                && mCurrentOperation.isStarted()) {
             Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
-            cancelInternal(mCurrentOperation);
-        }
-
-        startNextOperationIfIdle();
-    }
-
-    private void cancelInternal(Operation operation) {
-        if (operation != mCurrentOperation) {
-            Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
-            return;
-        }
-        if (!(operation.mClientMonitor instanceof Interruptable)) {
-            Slog.w(getTag(), "Operation not interruptable: " + operation);
-            return;
-        }
-        if (operation.mState == Operation.STATE_STARTED_CANCELING) {
-            Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
-            return;
-        }
-        if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
-            Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
-            // We can set it to null immediately, since the HAL was never notified to start.
-            if (mCurrentOperation != null) {
-                mCurrentOperation.mClientMonitor.destroy();
-            }
-            mCurrentOperation = null;
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
+        } else {
             startNextOperationIfIdle();
-            return;
         }
-        Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor);
-        final Interruptable interruptable = (Interruptable) operation.mClientMonitor;
-        interruptable.cancel();
-        operation.mState = Operation.STATE_STARTED_CANCELING;
-
-        // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
-        // forcibly finish this client.
-        mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
-                CancellationWatchdog.DELAY_MS);
     }
 
     /**
      * Requests to cancel enrollment.
      * @param token from the caller, should match the token passed in when requesting enrollment
      */
-    public void cancelEnrollment(IBinder token) {
-        if (mCurrentOperation == null) {
-            Slog.e(getTag(), "Unable to cancel enrollment, null operation");
-            return;
-        }
-        final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient;
-        final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
-        if (!isEnrolling || !tokenMatches) {
-            Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
-                    + " tokenMatches: " + tokenMatches);
-            return;
-        }
+    public void cancelEnrollment(IBinder token, long requestId) {
+        Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId);
 
-        cancelInternal(mCurrentOperation);
+        if (mCurrentOperation != null
+                && canCancelEnrollOperation(mCurrentOperation, token, requestId)) {
+            Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation);
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
+        } else {
+            for (BiometricSchedulerOperation operation : mPendingOperations) {
+                if (canCancelEnrollOperation(operation, token, requestId)) {
+                    Slog.d(getTag(), "Cancelling pending enrollment op: " + operation);
+                    operation.markCanceling();
+                }
+            }
+        }
     }
 
     /**
@@ -649,62 +424,42 @@
      * @param requestId the id returned when requesting authentication
      */
     public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
-        Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
-                + " current: " + mCurrentOperation
-                + " stack size: " + mPendingOperations.size());
+        Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId);
 
         if (mCurrentOperation != null
                 && canCancelAuthOperation(mCurrentOperation, token, requestId)) {
-            Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
-            cancelInternal(mCurrentOperation);
+            Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation);
+            mCurrentOperation.cancel(mHandler, mInternalCallback);
         } else {
-            // Look through the current queue for all authentication clients for the specified
-            // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
-            // all of them, instead of just the first one, since the API surface currently doesn't
-            // allow us to distinguish between multiple authentication requests from the same
-            // process. However, this generally does not happen anyway, and would be a class of
-            // bugs on its own.
-            for (Operation operation : mPendingOperations) {
+            for (BiometricSchedulerOperation operation : mPendingOperations) {
                 if (canCancelAuthOperation(operation, token, requestId)) {
-                    Slog.d(getTag(), "Marking " + operation
-                            + " as STATE_WAITING_IN_QUEUE_CANCELING");
-                    operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+                    Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation);
+                    operation.markCanceling();
                 }
             }
         }
     }
 
-    private static boolean canCancelAuthOperation(Operation operation, IBinder token,
-            long requestId) {
+    private static boolean canCancelEnrollOperation(BiometricSchedulerOperation operation,
+            IBinder token, long requestId) {
+        return operation.isEnrollOperation()
+                && operation.isMatchingToken(token)
+                && operation.isMatchingRequestId(requestId);
+    }
+
+    private static boolean canCancelAuthOperation(BiometricSchedulerOperation operation,
+            IBinder token, long requestId) {
         // TODO: restrict callers that can cancel without requestId (negative value)?
-        return isAuthenticationOrDetectionOperation(operation)
-                && operation.mClientMonitor.getToken() == token
-                && isMatchingRequestId(operation, requestId);
-    }
-
-    // By default, monitors are not associated with a request id to retain the original
-    // behavior (i.e. if no requestId is explicitly set then assume it matches)
-    private static boolean isMatchingRequestId(Operation operation, long requestId) {
-        return !operation.mClientMonitor.hasRequestId()
-                || operation.mClientMonitor.getRequestId() == requestId;
-    }
-
-    private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
-        final boolean isAuthentication =
-                operation.mClientMonitor instanceof AuthenticationConsumer;
-        final boolean isDetection =
-                operation.mClientMonitor instanceof DetectionConsumer;
-        return isAuthentication || isDetection;
+        return operation.isAuthenticationOrDetectionOperation()
+                && operation.isMatchingToken(token)
+                && operation.isMatchingRequestId(requestId);
     }
 
     /**
      * @return the current operation
      */
     public BaseClientMonitor getCurrentClient() {
-        if (mCurrentOperation == null) {
-            return null;
-        }
-        return mCurrentOperation.mClientMonitor;
+        return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null;
     }
 
     public int getCurrentPendingCount() {
@@ -719,7 +474,7 @@
                 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
         final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
         final List<String> pendingOperations = new ArrayList<>();
-        for (Operation operation : mPendingOperations) {
+        for (BiometricSchedulerOperation operation : mPendingOperations) {
             pendingOperations.add(operation.toString());
         }
 
@@ -735,7 +490,7 @@
         pw.println("Type: " + mSensorType);
         pw.println("Current operation: " + mCurrentOperation);
         pw.println("Pending operations: " + mPendingOperations.size());
-        for (Operation operation : mPendingOperations) {
+        for (BiometricSchedulerOperation operation : mPendingOperations) {
             pw.println("Pending operation: " + operation);
         }
         for (CrashState crashState : mCrashStates) {
@@ -746,7 +501,7 @@
     public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
         final ProtoOutputStream proto = new ProtoOutputStream();
         proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
-                ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
+                ? mCurrentOperation.getProtoEnum() : BiometricsProto.CM_NONE);
         proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
 
         if (!mRecentOperations.isEmpty()) {
@@ -771,6 +526,7 @@
      * HAL dies.
      */
     public void reset() {
+        Slog.d(getTag(), "Resetting scheduler");
         mPendingOperations.clear();
         mCurrentOperation = null;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
new file mode 100644
index 0000000..e8b50d9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains all the necessary information for a HAL operation.
+ */
+public class BiometricSchedulerOperation {
+    protected static final String TAG = "BiometricSchedulerOperation";
+
+    /**
+     * The operation is added to the list of pending operations and waiting for its turn.
+     */
+    protected static final int STATE_WAITING_IN_QUEUE = 0;
+
+    /**
+     * The operation is added to the list of pending operations, but a subsequent operation
+     * has been added. This state only applies to {@link Interruptable} operations. When this
+     * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+     */
+    protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+    /**
+     * The operation has reached the front of the queue and has started.
+     */
+    protected static final int STATE_STARTED = 2;
+
+    /**
+     * The operation was started, but is now canceling. Operations should wait for the HAL to
+     * acknowledge that the operation was canceled, at which point it finishes.
+     */
+    protected static final int STATE_STARTED_CANCELING = 3;
+
+    /**
+     * The operation has reached the head of the queue but is waiting for BiometricService
+     * to acknowledge and start the operation.
+     */
+    protected static final int STATE_WAITING_FOR_COOKIE = 4;
+
+    /**
+     * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
+     */
+    protected static final int STATE_FINISHED = 5;
+
+    @IntDef({STATE_WAITING_IN_QUEUE,
+            STATE_WAITING_IN_QUEUE_CANCELING,
+            STATE_STARTED,
+            STATE_STARTED_CANCELING,
+            STATE_WAITING_FOR_COOKIE,
+            STATE_FINISHED})
+    @Retention(RetentionPolicy.SOURCE)
+    protected @interface OperationState {}
+
+    private static final int CANCEL_WATCHDOG_DELAY_MS = 3000;
+
+    @NonNull
+    private final BaseClientMonitor mClientMonitor;
+    @Nullable
+    private final BaseClientMonitor.Callback mClientCallback;
+    @OperationState
+    private int mState;
+    @VisibleForTesting
+    @NonNull
+    final Runnable mCancelWatchdog;
+
+    BiometricSchedulerOperation(
+            @NonNull BaseClientMonitor clientMonitor,
+            @Nullable BaseClientMonitor.Callback callback
+    ) {
+        this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
+    }
+
+    protected BiometricSchedulerOperation(
+            @NonNull BaseClientMonitor clientMonitor,
+            @Nullable BaseClientMonitor.Callback callback,
+            @OperationState int state
+    ) {
+        mClientMonitor = clientMonitor;
+        mClientCallback = callback;
+        mState = state;
+        mCancelWatchdog = () -> {
+            if (!isFinished()) {
+                Slog.e(TAG, "[Watchdog Triggered]: " + this);
+                getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+            }
+        };
+    }
+
+    /**
+     * Zero if this operation is ready to start or has already started. A non-zero cookie
+     * is returned if the operation has not started and is waiting on
+     * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}.
+     *
+     * @return cookie or 0 if ready/started
+     */
+    public int isReadyToStart() {
+        if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) {
+            final int cookie = mClientMonitor.getCookie();
+            if (cookie != 0) {
+                mState = STATE_WAITING_FOR_COOKIE;
+            }
+            return cookie;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Start this operation without waiting for a cookie
+     * (i.e. {@link #isReadyToStart() returns zero}
+     *
+     * @param callback lifecycle callback
+     * @return if this operation started
+     */
+    public boolean start(@NonNull BaseClientMonitor.Callback callback) {
+        checkInState("start",
+                STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_WAITING_IN_QUEUE_CANCELING);
+
+        if (mClientMonitor.getCookie() != 0) {
+            throw new IllegalStateException("operation requires cookie");
+        }
+
+        return doStart(callback);
+    }
+
+    /**
+     * Start this operation after receiving the given cookie.
+     *
+     * @param callback lifecycle callback
+     * @param cookie   cookie indicting the operation should begin
+     * @return if this operation started
+     */
+    public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) {
+        checkInState("start",
+                STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_WAITING_IN_QUEUE_CANCELING);
+
+        if (mClientMonitor.getCookie() != cookie) {
+            Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie);
+            return false;
+        }
+
+        return doStart(callback);
+    }
+
+    private boolean doStart(@NonNull BaseClientMonitor.Callback callback) {
+        final BaseClientMonitor.Callback cb = getWrappedCallback(callback);
+
+        if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
+            Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
+
+            cb.onClientFinished(mClientMonitor, true /* success */);
+            if (mClientMonitor instanceof ErrorConsumer) {
+                final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor;
+                errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                        0 /* vendorCode */);
+            } else {
+                Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer");
+            }
+
+            return false;
+        }
+
+        if (isUnstartableHalOperation()) {
+            Slog.v(TAG, "unable to start: " + this);
+            ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+            cb.onClientFinished(mClientMonitor, false /* success */);
+            return false;
+        }
+
+        mState = STATE_STARTED;
+        mClientMonitor.start(cb);
+
+        Slog.v(TAG, "started: " + this);
+        return true;
+    }
+
+    /**
+     * Abort a pending operation.
+     *
+     * This is similar to cancel but the operation must not have been started. It will
+     * immediately abort the operation and notify the client that it has finished unsuccessfully.
+     */
+    public void abort() {
+        checkInState("cannot abort a non-pending operation",
+                STATE_WAITING_IN_QUEUE,
+                STATE_WAITING_FOR_COOKIE,
+                STATE_WAITING_IN_QUEUE_CANCELING);
+
+        if (isHalOperation()) {
+            ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+        }
+        getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+
+        Slog.v(TAG, "Aborted: " + this);
+    }
+
+    /** Flags this operation as canceled, if possible, but does not cancel it until started. */
+    public boolean markCanceling() {
+        if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) {
+            mState = STATE_WAITING_IN_QUEUE_CANCELING;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Cancel the operation now.
+     *
+     * @param handler handler to use for the cancellation watchdog
+     * @param callback lifecycle callback (only used if this operation hasn't started, otherwise
+     *                 the callback used from {@link #start(BaseClientMonitor.Callback)} is used)
+     */
+    public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) {
+        checkNotInState("cancel", STATE_FINISHED);
+
+        final int currentState = mState;
+        if (!isInterruptable()) {
+            Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this);
+            return;
+        }
+        if (currentState == STATE_STARTED_CANCELING) {
+            Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this);
+            return;
+        }
+
+        mState = STATE_STARTED_CANCELING;
+        if (currentState == STATE_WAITING_IN_QUEUE
+                || currentState == STATE_WAITING_IN_QUEUE_CANCELING
+                || currentState == STATE_WAITING_FOR_COOKIE) {
+            Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor);
+            ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback));
+        } else {
+            Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor);
+            ((Interruptable) mClientMonitor).cancel();
+        }
+
+        // forcibly finish this client if the HAL does not acknowledge within the timeout
+        handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS);
+    }
+
+    @NonNull
+    private BaseClientMonitor.Callback getWrappedCallback() {
+        return getWrappedCallback(null);
+    }
+
+    @NonNull
+    private BaseClientMonitor.Callback getWrappedCallback(
+            @Nullable BaseClientMonitor.Callback callback) {
+        final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() {
+            @Override
+            public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+                    boolean success) {
+                Slog.d(TAG, "[Finished / destroy]: " + clientMonitor);
+                mClientMonitor.destroy();
+                mState = STATE_FINISHED;
+            }
+        };
+        return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback);
+    }
+
+    /** {@link BaseClientMonitor#getSensorId()}. */
+    public int getSensorId() {
+        return mClientMonitor.getSensorId();
+    }
+
+    /** {@link BaseClientMonitor#getProtoEnum()}. */
+    public int getProtoEnum() {
+        return mClientMonitor.getProtoEnum();
+    }
+
+    /** {@link BaseClientMonitor#getTargetUserId()}. */
+    public int getTargetUserId() {
+        return mClientMonitor.getTargetUserId();
+    }
+
+    /** If the given clientMonitor is the same as the one in the constructor. */
+    public boolean isFor(@NonNull BaseClientMonitor clientMonitor) {
+        return mClientMonitor == clientMonitor;
+    }
+
+    /** If this operation is {@link Interruptable}. */
+    public boolean isInterruptable() {
+        return mClientMonitor instanceof Interruptable;
+    }
+
+    private boolean isHalOperation() {
+        return mClientMonitor instanceof HalClientMonitor<?>;
+    }
+
+    private boolean isUnstartableHalOperation() {
+        if (isHalOperation()) {
+            final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
+            if (client.getFreshDaemon() == null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** If this operation is an enrollment. */
+    public boolean isEnrollOperation() {
+        return mClientMonitor instanceof EnrollClient;
+    }
+
+    /** If this operation is authentication. */
+    public boolean isAuthenticateOperation() {
+        return mClientMonitor instanceof AuthenticationClient;
+    }
+
+    /** If this operation is authentication or detection. */
+    public boolean isAuthenticationOrDetectionOperation() {
+        final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer;
+        final boolean isDetection = mClientMonitor instanceof DetectionConsumer;
+        return isAuthentication || isDetection;
+    }
+
+    /** If this operation performs acquisition {@link AcquisitionClient}. */
+    public boolean isAcquisitionOperation() {
+        return mClientMonitor instanceof AcquisitionClient;
+    }
+
+    /**
+     * If this operation matches the original requestId.
+     *
+     * By default, monitors are not associated with a request id to retain the original
+     * behavior (i.e. if no requestId is explicitly set then assume it matches)
+     *
+     * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}.
+     */
+    public boolean isMatchingRequestId(long requestId) {
+        return !mClientMonitor.hasRequestId()
+                || mClientMonitor.getRequestId() == requestId;
+    }
+
+    /** If the token matches */
+    public boolean isMatchingToken(@Nullable IBinder token) {
+        return mClientMonitor.getToken() == token;
+    }
+
+    /** If this operation has started. */
+    public boolean isStarted() {
+        return mState == STATE_STARTED;
+    }
+
+    /** If this operation is cancelling but has not yet completed. */
+    public boolean isCanceling() {
+        return mState == STATE_STARTED_CANCELING;
+    }
+
+    /** If this operation has finished and completed its lifecycle. */
+    public boolean isFinished() {
+        return mState == STATE_FINISHED;
+    }
+
+    /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */
+    public boolean isMarkedCanceling() {
+        return mState == STATE_WAITING_IN_QUEUE_CANCELING;
+    }
+
+    /**
+     * The monitor passed to the constructor.
+     * @deprecated avoid using and move to encapsulate within the operation
+     */
+    @Deprecated
+    public BaseClientMonitor getClientMonitor() {
+        return mClientMonitor;
+    }
+
+    private void checkNotInState(String message, @OperationState int... states) {
+        for (int state : states) {
+            if (mState == state) {
+                throw new IllegalStateException(message + ": illegal state= " + state);
+            }
+        }
+    }
+
+    private void checkInState(String message, @OperationState int... states) {
+        for (int state : states) {
+            if (mState == state) {
+                return;
+            }
+        }
+        throw new IllegalStateException(message + ": illegal state= " + mState);
+    }
+
+    @Override
+    public String toString() {
+        return mClientMonitor + ", State: " + mState;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index fab98b6..d5093c75 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -32,6 +32,11 @@
      * {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
      * if the client is still waiting in the pending queue and got notified that a subsequent
      * operation is preempting it.
+     *
+     * This method must invoke
+     * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the
+     * given callback (with success).
+     *
      * @param callback invoked when the operation is completed.
      */
     void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index b056bf8..603cc22 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -16,10 +16,14 @@
 
 package com.android.server.biometrics.sensors;
 
+import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -68,9 +72,8 @@
                     return;
                 }
 
-                Slog.d(getTag(), "[Client finished] "
-                        + clientMonitor + ", success: " + success);
-                if (mCurrentOperation != null && mCurrentOperation.mClientMonitor == mOwner) {
+                Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
+                if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
                     mCurrentOperation = null;
                     startNextOperationIfIdle();
                 } else {
@@ -83,26 +86,30 @@
     }
 
     @VisibleForTesting
-    UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+    public UserAwareBiometricScheduler(@NonNull String tag,
+            @NonNull Handler handler,
+            @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull IBiometricService biometricService,
             @NonNull CurrentUserRetriever currentUserRetriever,
             @NonNull UserSwitchCallback userSwitchCallback,
             @NonNull CoexCoordinator coexCoordinator) {
-        super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
+        super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
                 LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
 
         mCurrentUserRetriever = currentUserRetriever;
         mUserSwitchCallback = userSwitchCallback;
     }
 
-    public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+    public UserAwareBiometricScheduler(@NonNull String tag,
+            @SensorType int sensorType,
             @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
             @NonNull CurrentUserRetriever currentUserRetriever,
             @NonNull UserSwitchCallback userSwitchCallback) {
-        this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
-                ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
-                userSwitchCallback, CoexCoordinator.getInstance());
+        this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher,
+                IBiometricService.Stub.asInterface(
+                        ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+                currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance());
     }
 
     @Override
@@ -122,7 +129,7 @@
         }
 
         final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
-        final int nextUserId = mPendingOperations.getFirst().mClientMonitor.getTargetUserId();
+        final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
 
         if (nextUserId == currentUserId) {
             super.startNextOperationIfIdle();
@@ -133,8 +140,8 @@
                     new ClientFinishedCallback(startClient);
 
             Slog.d(getTag(), "[Starting User] " + startClient);
-            mCurrentOperation = new Operation(
-                    startClient, finishedCallback, Operation.STATE_STARTED);
+            mCurrentOperation = new BiometricSchedulerOperation(
+                    startClient, finishedCallback, STATE_STARTED);
             startClient.start(finishedCallback);
         } else {
             if (mStopUserClient != null) {
@@ -147,8 +154,8 @@
 
                 Slog.d(getTag(), "[Stopping User] current: " + currentUserId
                         + ", next: " + nextUserId + ". " + mStopUserClient);
-                mCurrentOperation = new Operation(
-                        mStopUserClient, finishedCallback, Operation.STATE_STARTED);
+                mCurrentOperation = new BiometricSchedulerOperation(
+                        mStopUserClient, finishedCallback, STATE_STARTED);
                 mStopUserClient.start(finishedCallback);
             }
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 675ee545..039b08e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -213,7 +213,7 @@
         }
 
         @Override // Binder call
-        public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
+        public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -221,23 +221,24 @@
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for enroll");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+            return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
                     receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
         }
 
         @Override // Binder call
-        public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
+        public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 final int[] disabledFeatures) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
             // TODO(b/145027036): Implement this.
+            return -1;
         }
 
         @Override // Binder call
-        public void cancelEnrollment(final IBinder token) {
+        public void cancelEnrollment(final IBinder token, long requestId) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -246,7 +247,7 @@
                 return;
             }
 
-            provider.second.cancelEnrollment(provider.first, token);
+            provider.second.cancelEnrollment(provider.first, token, requestId);
         }
 
         @Override // Binder call
@@ -624,7 +625,7 @@
         private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
             for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
                 mServiceProviders.add(
-                        new Face10(getContext(), hidlSensor, mLockoutResetDispatcher));
+                        Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
             }
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index e099ba3..77e431c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -94,12 +94,12 @@
     void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull String opPackageName, long challenge);
 
-    void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+    long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
             int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
             @NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
             boolean debugConsent);
 
-    void cancelEnrollment(int sensorId, @NonNull IBinder token);
+    void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
 
     long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index a806277..aae4fbe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -82,13 +82,14 @@
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
+            @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
             @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
             @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
             boolean debugConsent) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
                 false /* shouldVibrate */);
+        setRequestId(requestId);
         mEnrollIgnoreList = getContext().getResources()
                 .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
         mEnrollIgnoreListVendor = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 4bae775..ae507ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -327,17 +327,18 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
             @NonNull String opPackageName, @NonNull int[] disabledFeatures,
             @Nullable Surface previewSurface, boolean debugConsent) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             final int maxTemplatesPerUser = mSensors.get(
                     sensorId).getSensorProperties().maxEnrollmentsPerUser;
             final FaceEnrollClient client = new FaceEnrollClient(mContext,
                     mSensors.get(sensorId).getLazySession(), token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
-                    opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
+                    opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
                     ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
                     debugConsent);
             scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@@ -351,11 +352,13 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() ->
+                mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 525e508..15d6a89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors.face.aidl;
 
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.face.EnrollmentStageConfig;
 import android.hardware.biometrics.face.Error;
 import android.hardware.biometrics.face.IFace;
@@ -188,6 +189,24 @@
                 Slog.w(TAG, "close");
                 cb.onSessionClosed();
             }
+
+            @Override
+            public ICancellationSignal authenticateWithContext(
+                    long operationId, OperationContext context) {
+                return authenticate(operationId);
+            }
+
+            @Override
+            public ICancellationSignal enrollWithContext(
+                    HardwareAuthToken hat, byte enrollmentType, byte[] features,
+                    NativeHandle previewSurface, OperationContext context) {
+                return enroll(hat, enrollmentType, features, previewSurface);
+            }
+
+            @Override
+            public ICancellationSignal detectInteractionWithContext(OperationContext context) {
+                return detectInteraction();
+            }
         };
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f4dcbbb..e957794 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -333,12 +333,13 @@
     Face10(@NonNull Context context,
             @NonNull FaceSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull Handler handler,
             @NonNull BiometricScheduler scheduler) {
         mSensorProperties = sensorProps;
         mContext = context;
         mSensorId = sensorProps.sensorId;
         mScheduler = scheduler;
-        mHandler = new Handler(Looper.getMainLooper());
+        mHandler = handler;
         mUsageStats = new UsageStats(context);
         mAuthenticatorIds = new HashMap<>();
         mLazyDaemon = Face10.this::getDaemon;
@@ -357,9 +358,11 @@
         }
     }
 
-    public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
+    public static Face10 newInstance(@NonNull Context context,
+            @NonNull FaceSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
-        this(context, sensorProps, lockoutResetDispatcher,
+        final Handler handler = new Handler(Looper.getMainLooper());
+        return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
                 new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
                         null /* gestureAvailabilityTracker */));
     }
@@ -573,10 +576,11 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
             @NonNull String opPackageName, @NonNull int[] disabledFeatures,
             @Nullable Surface previewSurface, boolean debugConsent) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -584,7 +588,7 @@
 
             final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
-                    opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+                    opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
                     ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
 
             mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -598,13 +602,12 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> {
-            mScheduler.cancelEnrollment(token);
-        });
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
     }
 
     @Override
@@ -893,6 +896,8 @@
                     boolean success) {
                 if (success) {
                     mCurrentUserId = targetUserId;
+                } else {
+                    Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
                 }
             }
         });
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 80828cced..31e5c86 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -53,12 +53,13 @@
 
     FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
             @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
             @Nullable Surface previewSurface, int sensorId) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
                 false /* shouldVibrate */);
+        setRequestId(requestId);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
         mEnrollIgnoreList = getContext().getResources()
                 .getIntArray(R.array.config_face_acquire_enroll_ignorelist);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 3e70ee5..6366e19 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -249,7 +249,7 @@
         }
 
         @Override // Binder call
-        public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
+        public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
                 final int userId, final IFingerprintServiceReceiver receiver,
                 final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
@@ -257,15 +257,15 @@
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
             if (provider == null) {
                 Slog.w(TAG, "Null provider for enroll");
-                return;
+                return -1;
             }
 
-            provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+            return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
                     receiver, opPackageName, enrollReason);
         }
 
         @Override // Binder call
-        public void cancelEnrollment(final IBinder token) {
+        public void cancelEnrollment(final IBinder token, long requestId) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -274,7 +274,7 @@
                 return;
             }
 
-            provider.second.cancelEnrollment(provider.first, token);
+            provider.second.cancelEnrollment(provider.first, token, requestId);
         }
 
         @SuppressWarnings("deprecation")
@@ -818,7 +818,7 @@
                             mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                 } else {
                     fingerprint21 = Fingerprint21.newInstance(getContext(),
-                            mFingerprintStateCallback, hidlSensor,
+                            mFingerprintStateCallback, hidlSensor, mHandler,
                             mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                 }
                 mServiceProviders.add(fingerprint21);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 1772f81..535705c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -88,11 +88,11 @@
     /**
      * Schedules fingerprint enrollment.
      */
-    void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+    long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
             int userId, @NonNull IFingerprintServiceReceiver receiver,
             @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
 
-    void cancelEnrollment(int sensorId, @NonNull IBinder token);
+    void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
 
     long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index ccb34aa..67507cc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -57,7 +57,7 @@
     private boolean mIsPointerDown;
 
     FingerprintEnrollClient(@NonNull Context context,
-            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, long requestId,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@@ -69,6 +69,7 @@
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
                 !sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+        setRequestId(requestId);
         mSensorProps = sensorProps;
         mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
         mMaxTemplatesPerUser = maxTemplatesPerUser;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 734b173..eb16c76 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -347,15 +347,16 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
             @FingerprintManager.EnrollReason int enrollReason) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
                     .maxEnrollmentsPerUser;
             final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
-                    mSensors.get(sensorId).getLazySession(), token,
+                    mSensors.get(sensorId).getLazySession(), token, id,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
                     opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
                     mSensors.get(sensorId).getSensorProperties(),
@@ -378,11 +379,13 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() ->
+                mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index e771923..1eb153c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -17,10 +17,12 @@
 package com.android.server.biometrics.sensors.fingerprint.aidl;
 
 import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
 import android.hardware.biometrics.fingerprint.Error;
 import android.hardware.biometrics.fingerprint.IFingerprint;
 import android.hardware.biometrics.fingerprint.ISession;
 import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.biometrics.fingerprint.PointerContext;
 import android.hardware.biometrics.fingerprint.SensorProps;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.os.RemoteException;
@@ -182,6 +184,34 @@
             public void onUiReady() {
                 Slog.w(TAG, "onUiReady");
             }
+
+            @Override
+            public ICancellationSignal authenticateWithContext(
+                    long operationId, OperationContext context) {
+                return authenticate(operationId);
+            }
+
+            @Override
+            public ICancellationSignal enrollWithContext(
+                    HardwareAuthToken hat, OperationContext context) {
+                return enroll(hat);
+            }
+
+            @Override
+            public ICancellationSignal detectInteractionWithContext(OperationContext context) {
+                return detectInteraction();
+            }
+
+            @Override
+            public void onPointerDownWithContext(PointerContext context) {
+                onPointerDown(
+                        context.pointerId, context.x, context.y, context.minor, context.major);
+            }
+
+            @Override
+            public void onPointerUpWithContext(PointerContext context) {
+                onPointerUp(context.pointerId);
+            }
         };
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 5f2f4cf..6feb5fa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -42,7 +42,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IHwBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -320,7 +319,8 @@
     Fingerprint21(@NonNull Context context,
             @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
-            @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
+            @NonNull BiometricScheduler scheduler,
+            @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull HalResultController controller) {
         mContext = context;
@@ -356,16 +356,15 @@
     public static Fingerprint21 newInstance(@NonNull Context context,
             @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
+            @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-        final Handler handler = new Handler(Looper.getMainLooper());
         final BiometricScheduler scheduler =
                 new BiometricScheduler(TAG,
                         BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
                         gestureAvailabilityDispatcher);
         final HalResultController controller = new HalResultController(sensorProps.sensorId,
-                context, handler,
-                scheduler);
+                context, handler, scheduler);
         return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
                 lockoutResetDispatcher, controller);
     }
@@ -491,19 +490,25 @@
                 !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
         final FingerprintUpdateActiveUserClient client =
                 new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
-                        mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
-                        hasEnrolled, mAuthenticatorIds, force);
+                        mContext.getOpPackageName(), mSensorProperties.sensorId,
+                        this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
         mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
             @Override
             public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                     boolean success) {
                 if (success) {
                     mCurrentUserId = targetUserId;
+                } else {
+                    Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
                 }
             }
         });
     }
 
+    private int getCurrentUser() {
+        return mCurrentUserId;
+    }
+
     @Override
     public boolean containsSensor(int sensorId) {
         return mSensorProperties.sensorId == sensorId;
@@ -558,18 +563,20 @@
     }
 
     @Override
-    public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+    public long scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
             @FingerprintManager.EnrollReason int enrollReason) {
+        final long id = mRequestCounter.incrementAndGet();
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
             final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
-                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
-                    hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
-                    ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
-                    mSidefpsController, enrollReason);
+                    mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+                    userId, hardwareAuthToken, opPackageName,
+                    FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+                    mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+                    enrollReason);
             mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -588,13 +595,12 @@
                 }
             });
         });
+        return id;
     }
 
     @Override
-    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-        mHandler.post(() -> {
-            mScheduler.cancelEnrollment(token);
-        });
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+        mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index dd68b4d..273f8a5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -26,7 +26,6 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.FingerprintStateListener;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Handler;
 import android.os.IBinder;
@@ -135,43 +134,16 @@
     @NonNull private final RestartAuthRunnable mRestartAuthRunnable;
 
     private static class TestableBiometricScheduler extends BiometricScheduler {
-        @NonNull private final TestableInternalCallback mInternalCallback;
         @NonNull private Fingerprint21UdfpsMock mFingerprint21;
 
-        TestableBiometricScheduler(@NonNull String tag,
+        TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler,
                 @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-            super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
-                    gestureAvailabilityDispatcher);
-            mInternalCallback = new TestableInternalCallback();
-        }
-
-        class TestableInternalCallback extends InternalCallback {
-            @Override
-            public void onClientStarted(BaseClientMonitor clientMonitor) {
-                super.onClientStarted(clientMonitor);
-                Slog.d(TAG, "Client started: " + clientMonitor);
-                mFingerprint21.setDebugMessage("Started: " + clientMonitor);
-            }
-
-            @Override
-            public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
-                super.onClientFinished(clientMonitor, success);
-                Slog.d(TAG, "Client finished: " + clientMonitor);
-                mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
-            }
+            super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
         }
 
         void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
             mFingerprint21 = fingerprint21;
         }
-
-        /**
-         * Expose the internal finish callback so it can be used for testing
-         */
-        @Override
-        @NonNull protected InternalCallback getInternalCallback() {
-            return mInternalCallback;
-        }
     }
 
     /**
@@ -280,7 +252,7 @@
 
         final Handler handler = new Handler(Looper.getMainLooper());
         final TestableBiometricScheduler scheduler =
-                new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
+                new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
         final MockHalResultController controller =
                 new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
         return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1ebf44c..cc50bdf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -55,7 +55,7 @@
 
     FingerprintEnrollClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
-            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull byte[] hardwareAuthToken, @NonNull String owner,
             @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -64,6 +64,7 @@
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
                 timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
                 true /* shouldVibrate */);
+        setRequestId(requestId);
         mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
 
         mEnrollReason = enrollReason;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index fd38bdd..a2c1892 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -31,6 +31,7 @@
 
 import java.io.File;
 import java.util.Map;
+import java.util.function.Supplier;
 
 /**
  * Sets the HAL's current active user, and updates the framework's authenticatorId cache.
@@ -40,7 +41,7 @@
     private static final String TAG = "FingerprintUpdateActiveUserClient";
     private static final String FP_DATA_DIR = "fpdata";
 
-    private final int mCurrentUserId;
+    private final Supplier<Integer> mCurrentUserId;
     private final boolean mForceUpdateAuthenticatorId;
     private final boolean mHasEnrolledBiometrics;
     private final Map<Integer, Long> mAuthenticatorIds;
@@ -48,8 +49,9 @@
 
     FingerprintUpdateActiveUserClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
-            @NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics,
-            @NonNull Map<Integer, Long> authenticatorIds, boolean forceUpdateAuthenticatorId) {
+            @NonNull String owner, int sensorId, Supplier<Integer> currentUserId,
+            boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds,
+            boolean forceUpdateAuthenticatorId) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
                 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
                 BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
@@ -63,7 +65,7 @@
     public void start(@NonNull Callback callback) {
         super.start(callback);
 
-        if (mCurrentUserId == getTargetUserId() && !mForceUpdateAuthenticatorId) {
+        if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
             Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
             callback.onClientFinished(this, true /* success */);
             return;
@@ -109,8 +111,10 @@
     @Override
     protected void startHalOperation() {
         try {
-            getFreshDaemon().setActiveGroup(getTargetUserId(), mDirectory.getAbsolutePath());
-            mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+            final int targetId = getTargetUserId();
+            Slog.d(TAG, "Setting active user: " + targetId);
+            getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath());
+            mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
                     ? getFreshDaemon().getAuthenticatorId() : 0L);
             mCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
index 8c93891..6654c0c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
@@ -189,7 +189,8 @@
                 removed.add(id);
             }
         }
-        if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()) {
+        if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()
+                && !chunk.isPurge()) {
             return null;
         }
         mComplete = chunk.isComplete();
@@ -239,9 +240,10 @@
         }
 
         // Determine number of chunks we need to send.
-        int numChunks = 0;
+        int numChunks = purge ? 1 : 0;
         if (modified != null) {
-            numChunks = roundUpFraction(modified.size(), maxNumModifiedPerChunk);
+            numChunks = Math.max(numChunks,
+                    roundUpFraction(modified.size(), maxNumModifiedPerChunk));
         }
         if (removed != null) {
             numChunks = Math.max(numChunks, roundUpFraction(removed.size(), maxNumRemovedPerChunk));
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 39fa3f2..135276e 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -50,6 +50,12 @@
     public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
 
     /**
+     * Returns the flags that should be added to any virtual displays created on this virtual
+     * device.
+     */
+    public abstract int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice);
+
+    /**
      * Returns true if the given {@code uid} is the owner of any virtual devices that are
      * currently active.
      */
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index 880dbf6..fe002ce 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -25,6 +25,7 @@
 import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
 import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
 
+import static java.util.Collections.emptyMap;
 import static java.util.Collections.emptySet;
 
 import android.annotation.NonNull;
@@ -157,17 +158,17 @@
         Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove =
                 new ArrayMap<>();
         for (String packageName : packageNames) {
-            Long versionCode = getVersionCodeOrNull(packageName);
-            if (versionCode == null) {
-                // Package isn't installed yet.
-                continue;
-            }
-
             Set<Long> changeIdsToSkip = packageToChangeIdsToSkip.getOrDefault(packageName,
                     emptySet());
-            Map<Long, PackageOverride> overridesToAdd = mOverridesParser.parsePackageOverrides(
-                    properties.getString(packageName, /* defaultValue= */ ""), packageName,
-                    versionCode, changeIdsToSkip);
+
+            Map<Long, PackageOverride> overridesToAdd = emptyMap();
+            Long versionCode = getVersionCodeOrNull(packageName);
+            if (versionCode != null) {
+                // Only if package installed add overrides, otherwise just remove.
+                overridesToAdd = mOverridesParser.parsePackageOverrides(
+                        properties.getString(packageName, /* defaultValue= */ ""), packageName,
+                        versionCode, changeIdsToSkip);
+            }
             if (!overridesToAdd.isEmpty()) {
                 packageNameToOverridesToAdd.put(packageName,
                         new CompatibilityOverrideConfig(overridesToAdd));
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index cc9efbc..fce6737 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -199,6 +199,7 @@
         private final NetworkTemplate mNetworkTemplate;
         private final UsageCallback mUsageCallback;
         private NetworkCapabilities mNetworkCapabilities;
+        private final NetworkStatsManager mStatsManager;
 
         public MultipathTracker(Network network, NetworkCapabilities nc) {
             this.network = network;
@@ -238,6 +239,13 @@
                     updateMultipathBudget();
                 }
             };
+            mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+            // Query stats from NetworkStatsService will trigger a poll by default.
+            // But since MultipathPolicyTracker listens NPMS events that triggered by
+            // stats updated event, and will query stats
+            // after the event. A polling -> updated -> query -> polling loop will be introduced
+            // if polls on open. Hence, set flag to false to prevent a polling loop.
+            mStatsManager.setPollOnOpen(false);
 
             updateMultipathBudget();
         }
@@ -262,8 +270,7 @@
         private long getNetworkTotalBytes(long start, long end) {
             try {
                 final android.app.usage.NetworkStats.Bucket ret =
-                        mContext.getSystemService(NetworkStatsManager.class)
-                        .querySummaryForDevice(mNetworkTemplate, start, end);
+                        mStatsManager.querySummaryForDevice(mNetworkTemplate, start, end);
                 return ret.getRxBytes() + ret.getTxBytes();
             } catch (RuntimeException e) {
                 Log.w(TAG, "Failed to get data usage: " + e);
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 7fe24ff..78d55b9 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -43,14 +43,14 @@
  */
 public final class DeviceState {
     /**
-     * Flag that indicates sticky requests should be cancelled when this device state becomes the
+     * Flag that indicates override requests should be cancelled when this device state becomes the
      * base device state.
      */
-    public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0;
+    public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
 
     /** @hide */
     @IntDef(prefix = {"FLAG_"}, flag = true, value = {
-            FLAG_CANCEL_STICKY_REQUESTS,
+            FLAG_CANCEL_OVERRIDE_REQUESTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeviceStateFlags {}
@@ -114,4 +114,10 @@
     public int hashCode() {
         return Objects.hash(mIdentifier, mName, mFlags);
     }
+
+    /** Checks if a specific flag is set
+     */
+    public boolean hasFlag(int flagToCheckFor) {
+        return (mFlags & flagToCheckFor) == flagToCheckFor;
+    }
 }
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 792feea..709af91 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -20,6 +20,7 @@
 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
 
+import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
@@ -273,14 +274,14 @@
         synchronized (mLock) {
             final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
 
-            // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS
+            // Whether or not at least one device state has the flag FLAG_CANCEL_OVERRIDE_REQUESTS
             // set. If set to true, the OverrideRequestController will be configured to allow sticky
             // requests.
             boolean hasTerminalDeviceState = false;
             mDeviceStates.clear();
             for (int i = 0; i < supportedDeviceStates.length; i++) {
                 DeviceState state = supportedDeviceStates[i];
-                if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
+                if (state.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
                     hasTerminalDeviceState = true;
                 }
                 mDeviceStates.put(state.getIdentifier(), state);
@@ -345,8 +346,8 @@
             }
             mBaseState = Optional.of(baseState);
 
-            if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
-                mOverrideRequestController.cancelStickyRequests();
+            if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
+                mOverrideRequestController.cancelOverrideRequests();
             }
             mOverrideRequestController.handleBaseStateChanged();
             updatePendingStateLocked();
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 05c9eb2..36cb416 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -153,6 +153,16 @@
     }
 
     /**
+     * Cancels all override requests, this could be due to the device being put
+     * into a hardware state that declares the flag "FLAG_CANCEL_OVERRIDE_REQUESTS"
+     */
+    void cancelOverrideRequests() {
+        mTmpRequestsToCancel.clear();
+        mTmpRequestsToCancel.addAll(mRequests);
+        cancelRequestsLocked(mTmpRequestsToCancel);
+    }
+
+    /**
      * Returns {@code true} if this controller is current managing a request with the specified
      * {@code token}, {@code false} otherwise.
      */
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index c04032f..f4c36c6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -54,6 +54,10 @@
 
     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
 
+    public static final int AUTO_BRIGHTNESS_ENABLED = 1;
+    public static final int AUTO_BRIGHTNESS_DISABLED = 2;
+    public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3;
+
     // How long the current sensor reading is assumed to be valid beyond the current time.
     // This provides a bit of prediction, as well as ensures that the weight for the last sample is
     // non-zero, which in turn ensures that the total weight is non-zero.
@@ -70,13 +74,6 @@
     private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
     private static final int MSG_RUN_UPDATE = 6;
 
-    // Length of the ambient light horizon used to calculate the long term estimate of ambient
-    // light.
-    private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
-
-    // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
-    private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
-
     // Callbacks for requesting updates to the display's power state
     private final Callbacks mCallbacks;
 
@@ -121,8 +118,10 @@
     // and only then decide whether to change brightness.
     private final boolean mResetAmbientLuxAfterWarmUpConfig;
 
-    // Period of time in which to consider light samples in milliseconds.
-    private final int mAmbientLightHorizon;
+    // Period of time in which to consider light samples for a short/long-term estimate of ambient
+    // light in milliseconds.
+    private final int mAmbientLightHorizonLong;
+    private final int mAmbientLightHorizonShort;
 
     // The intercept used for the weighting calculation. This is used in order to keep all possible
     // weighting values positive.
@@ -214,7 +213,9 @@
     private IActivityTaskManager mActivityTaskManager;
     private PackageManager mPackageManager;
     private Context mContext;
+    private int mState = AUTO_BRIGHTNESS_DISABLED;
 
+    private Clock mClock;
     private final Injector mInjector;
 
     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
@@ -226,14 +227,16 @@
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, Context context,
             HighBrightnessModeController hbmController,
-            BrightnessMappingStrategy idleModeBrightnessMapper) {
+            BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
+            int ambientLightHorizonLong) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor,
                 interactiveModeBrightnessMapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
                 ambientBrightnessThresholds, screenBrightnessThresholds, context,
-                hbmController, idleModeBrightnessMapper
+                hbmController, idleModeBrightnessMapper, ambientLightHorizonShort,
+                ambientLightHorizonLong
         );
     }
 
@@ -247,8 +250,10 @@
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, Context context,
             HighBrightnessModeController hbmController,
-            BrightnessMappingStrategy idleModeBrightnessMapper) {
+            BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
+            int ambientLightHorizonLong) {
         mInjector = injector;
+        mClock = injector.createClock();
         mContext = context;
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
@@ -263,15 +268,16 @@
         mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
         mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
-        mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
-        mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
+        mAmbientLightHorizonLong = ambientLightHorizonLong;
+        mAmbientLightHorizonShort = ambientLightHorizonShort;
+        mWeightingIntercept = ambientLightHorizonLong;
         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
         mScreenBrightnessThresholds = screenBrightnessThresholds;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
         mHandler = new AutomaticBrightnessHandler(looper);
         mAmbientLightRingBuffer =
-            new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
+            new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock);
 
         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
             mLightSensor = lightSensor;
@@ -331,10 +337,11 @@
         return mCurrentBrightnessMapper.getAutoBrightnessAdjustment();
     }
 
-    public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
+    public void configure(int state, @Nullable BrightnessConfiguration configuration,
             float brightness, boolean userChangedBrightness, float adjustment,
             boolean userChangedAutoBrightnessAdjustment, int displayPolicy) {
-        mHbmController.setAutoBrightnessEnabled(enable);
+        mState = state;
+        mHbmController.setAutoBrightnessEnabled(mState);
         // While dozing, the application processor may be suspended which will prevent us from
         // receiving new information from the light sensor. On some devices, we may be able to
         // switch to a wake-up light sensor instead but for now we will simply disable the sensor
@@ -346,6 +353,7 @@
         if (userChangedAutoBrightnessAdjustment) {
             changed |= setAutoBrightnessAdjustment(adjustment);
         }
+        final boolean enable = mState == AUTO_BRIGHTNESS_ENABLED;
         if (userChangedBrightness && enable) {
             // Update the brightness curve with the new user control point. It's critical this
             // happens after we update the autobrightness adjustment since it may reset it.
@@ -390,6 +398,11 @@
         mHandler.sendEmptyMessage(MSG_RUN_UPDATE);
     }
 
+    @VisibleForTesting
+    float getAmbientLux() {
+        return mAmbientLux;
+    }
+
     private boolean setDisplayPolicy(int policy) {
         if (mDisplayPolicy == policy) {
             return false;
@@ -459,6 +472,7 @@
     public void dump(PrintWriter pw) {
         pw.println();
         pw.println("Automatic Brightness Controller Configuration:");
+        pw.println("  mState=" + configStateToString(mState));
         pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
         pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
         pw.println("  mDozeScaleFactor=" + mDozeScaleFactor);
@@ -468,7 +482,8 @@
         pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
         pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
         pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
-        pw.println("  mAmbientLightHorizon=" + mAmbientLightHorizon);
+        pw.println("  mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
+        pw.println("  mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
         pw.println("  mWeightingIntercept=" + mWeightingIntercept);
 
         pw.println();
@@ -520,11 +535,24 @@
         mScreenBrightnessThresholds.dump(pw);
     }
 
+    private String configStateToString(int state) {
+        switch (state) {
+        case AUTO_BRIGHTNESS_ENABLED:
+            return "AUTO_BRIGHTNESS_ENABLED";
+        case AUTO_BRIGHTNESS_DISABLED:
+            return "AUTO_BRIGHTNESS_DISABLED";
+        case AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE:
+            return "AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE";
+        default:
+            return String.valueOf(state);
+        }
+    }
+
     private boolean setLightSensorEnabled(boolean enable) {
         if (enable) {
             if (!mLightSensorEnabled) {
                 mLightSensorEnabled = true;
-                mLightSensorEnableTime = SystemClock.uptimeMillis();
+                mLightSensorEnableTime = mClock.uptimeMillis();
                 mCurrentLightSensorRate = mInitialLightSensorRate;
                 registerForegroundAppUpdater();
                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
@@ -559,7 +587,7 @@
 
     private void applyLightSensorMeasurement(long time, float lux) {
         mRecentLightSamples++;
-        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
+        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
         mAmbientLightRingBuffer.push(time, lux);
 
         // Remember this sample value.
@@ -700,8 +728,8 @@
     }
 
     private void updateAmbientLux() {
-        long time = SystemClock.uptimeMillis();
-        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
+        long time = mClock.uptimeMillis();
+        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
         updateAmbientLux(time);
     }
 
@@ -721,7 +749,7 @@
                         timeWhenSensorWarmedUp);
                 return;
             }
-            setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
+            setAmbientLux(calculateAmbientLux(time, mAmbientLightHorizonShort));
             mAmbientLuxValid = true;
             if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
@@ -741,8 +769,8 @@
         // proposed ambient light value since the slow value might be sufficiently far enough away
         // from the fast value to cause a recalculation while its actually just converging on
         // the fast value still.
-        float slowAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_LONG_HORIZON_MILLIS);
-        float fastAmbientLux = calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS);
+        float slowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
+        float fastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
 
         if ((slowAmbientLux >= mAmbientBrighteningThreshold
                 && fastAmbientLux >= mAmbientBrighteningThreshold
@@ -1023,7 +1051,7 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             if (mLightSensorEnabled) {
-                final long time = SystemClock.uptimeMillis();
+                final long time = mClock.uptimeMillis();
                 final float lux = event.values[0];
                 handleLightSensorEvent(time, lux);
             }
@@ -1049,6 +1077,15 @@
         void updateBrightness();
     }
 
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /**
+         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+         */
+        long uptimeMillis();
+    }
+
     /**
      * A ring buffer of ambient light measurements sorted by time.
      *
@@ -1068,14 +1105,16 @@
         private int mStart;
         private int mEnd;
         private int mCount;
+        Clock mClock;
 
-        public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+        public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon, Clock clock) {
             if (lightSensorRate <= 0) {
                 throw new IllegalArgumentException("lightSensorRate must be above 0");
             }
             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
             mRingLux = new float[mCapacity];
             mRingTime = new long[mCapacity];
+            mClock = clock;
         }
 
         public float getLux(int index) {
@@ -1160,7 +1199,7 @@
             StringBuilder buf = new StringBuilder();
             buf.append('[');
             for (int i = 0; i < mCount; i++) {
-                final long next = i + 1 < mCount ? getTime(i + 1) : SystemClock.uptimeMillis();
+                final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
                 if (i != 0) {
                     buf.append(", ");
                 }
@@ -1189,5 +1228,9 @@
         public Handler getBackgroundThreadHandler() {
             return BackgroundThread.getHandler();
         }
+
+        Clock createClock() {
+            return SystemClock::uptimeMillis;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index def9685..d0ce9ef 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -37,6 +37,8 @@
  * </p>
  */
 abstract class DisplayDevice {
+    private static final Display.Mode EMPTY_DISPLAY_MODE = new Display.Mode.Builder().build();
+
     private final DisplayAdapter mDisplayAdapter;
     private final IBinder mDisplayToken;
     private final String mUniqueId;
@@ -213,6 +215,13 @@
     public void setUserPreferredDisplayModeLocked(Display.Mode mode) { }
 
     /**
+     * Returns the user preferred display mode.
+     */
+    public Display.Mode getUserPreferredDisplayModeLocked() {
+        return EMPTY_DISPLAY_MODE;
+    }
+
+    /**
      * Sets the requested color mode.
      */
     public void setRequestedColorModeLocked(int colorMode) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a9e1647..3df2422 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -52,6 +52,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -86,6 +87,13 @@
 
     private static final float NITS_INVALID = -1;
 
+    // Length of the ambient light horizon used to calculate the long term estimate of ambient
+    // light.
+    private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
+
+    // Length of the ambient light horizon used to calculate short-term estimate of ambient light.
+    private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
+
     private final Context mContext;
 
     // The details of the ambient light sensor associated with this display.
@@ -120,6 +128,8 @@
     private float mBrightnessRampFastIncrease = Float.NaN;
     private float mBrightnessRampSlowDecrease = Float.NaN;
     private float mBrightnessRampSlowIncrease = Float.NaN;
+    private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
+    private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
     private Spline mBrightnessToBacklightSpline;
     private Spline mBacklightToBrightnessSpline;
     private Spline mBacklightToNitsSpline;
@@ -346,6 +356,14 @@
         return mBrightnessRampSlowIncrease;
     }
 
+    public int getAmbientHorizonLong() {
+        return mAmbientHorizonLong;
+    }
+
+    public int getAmbientHorizonShort() {
+        return mAmbientHorizonShort;
+    }
+
     SensorData getAmbientLightSensor() {
         return mAmbientLightSensor;
     }
@@ -405,6 +423,8 @@
                 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
                 + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
                 + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+                + ", mAmbientHorizonLong=" + mAmbientHorizonLong
+                + ", mAmbientHorizonShort=" + mAmbientHorizonShort
                 + ", mAmbientLightSensor=" + mAmbientLightSensor
                 + ", mProximitySensor=" + mProximitySensor
                 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -461,6 +481,7 @@
                 loadBrightnessRamps(config);
                 loadAmbientLightSensorFromDdc(config);
                 loadProxSensorFromDdc(config);
+                loadAmbientHorizonFromDdc(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
@@ -869,6 +890,17 @@
         }
     }
 
+    private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
+        final BigInteger configLongHorizon = config.getAmbientLightHorizonLong();
+        if (configLongHorizon != null) {
+            mAmbientHorizonLong = configLongHorizon.intValue();
+        }
+        final BigInteger configShortHorizon = config.getAmbientLightHorizonShort();
+        if (configShortHorizon != null) {
+            mAmbientHorizonShort = configShortHorizon.intValue();
+        }
+    }
+
     static class SensorData {
         public String type;
         public String name;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index be889e4..3feffc6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -873,11 +873,11 @@
 
     private void updateUserPreferredDisplayModeSettingsLocked() {
         final float refreshRate = Settings.Global.getFloat(mContext.getContentResolver(),
-                Settings.Global.USER_PREFERRED_REFRESH_RATE, 0.0f);
+                Settings.Global.USER_PREFERRED_REFRESH_RATE, Display.INVALID_DISPLAY_REFRESH_RATE);
         final int height = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, -1);
+                Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, Display.INVALID_DISPLAY_HEIGHT);
         final int width = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, -1);
+                Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, Display.INVALID_DISPLAY_WIDTH);
         Display.Mode mode = new Display.Mode(width, height, refreshRate);
         mUserPreferredMode = isResolutionAndRefreshRateValid(mode) ? mode : null;
     }
@@ -1250,6 +1250,14 @@
         }
         final Surface surface = virtualDisplayConfig.getSurface();
         int flags = virtualDisplayConfig.getFlags();
+        if (virtualDevice != null) {
+            final VirtualDeviceManagerInternal vdm =
+                    getLocalService(VirtualDeviceManagerInternal.class);
+            if (!vdm.isValidVirtualDevice(virtualDevice)) {
+                throw new SecurityException("Invalid virtual device");
+            }
+            flags |= vdm.getBaseVirtualDisplayFlags(virtualDevice);
+        }
 
         if (surface != null && surface.isSingleBuffered()) {
             throw new IllegalArgumentException("Surface can't be single-buffered");
@@ -1282,14 +1290,6 @@
             }
         }
 
-        if (virtualDevice != null) {
-            final VirtualDeviceManagerInternal vdm =
-                    getLocalService(VirtualDeviceManagerInternal.class);
-            if (!vdm.isValidVirtualDevice(virtualDevice)) {
-                throw new SecurityException("Invalid virtual device");
-            }
-        }
-
         if (callingUid != Process.SYSTEM_UID
                 && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
             if (!canProjectVideo(projection)) {
@@ -1560,8 +1560,11 @@
         }
         if (mUserPreferredMode != null) {
             device.setUserPreferredDisplayModeLocked(mUserPreferredMode);
+        } else {
+            configurePreferredDisplayModeLocked(display);
         }
         addDisplayPowerControllerLocked(display);
+
         mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
 
         final float brightnessDefault = display.getDisplayInfoLocked().brightnessDefault;
@@ -1688,6 +1691,24 @@
         }
     }
 
+    private void configurePreferredDisplayModeLocked(LogicalDisplay display) {
+        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+        final Point userPreferredResolution =
+                mPersistentDataStore.getUserPreferredResolution(device);
+        final float refreshRate = mPersistentDataStore.getUserPreferredRefreshRate(device);
+        if (userPreferredResolution == null && Float.isNaN(refreshRate)) {
+            return;
+        }
+        Display.Mode.Builder modeBuilder = new Display.Mode.Builder();
+        if (userPreferredResolution != null) {
+            modeBuilder.setResolution(userPreferredResolution.x, userPreferredResolution.y);
+        }
+        if (!Float.isNaN(refreshRate)) {
+            modeBuilder.setRefreshRate(refreshRate);
+        }
+        device.setUserPreferredDisplayModeLocked(modeBuilder.build());
+    }
+
     // If we've never recorded stable device stats for this device before and they aren't
     // explicitly configured, go ahead and record the stable device stats now based on the status
     // of the default display at first boot.
@@ -1731,36 +1752,79 @@
         return mWideColorSpace.getId();
     }
 
-    void setUserPreferredDisplayModeInternal(Display.Mode mode) {
+    void setUserPreferredDisplayModeInternal(int displayId, Display.Mode mode) {
         synchronized (mSyncRoot) {
-            if (Objects.equals(mUserPreferredMode, mode)) {
+            if (Objects.equals(mUserPreferredMode, mode) && displayId == Display.INVALID_DISPLAY) {
                 return;
             }
 
-            if (mode != null && !isResolutionAndRefreshRateValid(mode)) {
+            if (mode != null && !isResolutionAndRefreshRateValid(mode)
+                    && displayId == Display.INVALID_DISPLAY) {
                 throw new IllegalArgumentException("width, height and refresh rate of mode should "
-                        + "be greater than 0");
+                        + "be greater than 0 when setting the global user preferred display mode.");
             }
-            mUserPreferredMode = mode;
 
-            final int resolutionHeight = mode == null ? -1 : mode.getPhysicalHeight();
-            final int resolutionWidth = mode == null ? -1 : mode.getPhysicalWidth();
-            final float refreshRate = mode == null ? 0.0f : mode.getRefreshRate();
-            Settings.Global.putFloat(mContext.getContentResolver(),
-                    Settings.Global.USER_PREFERRED_REFRESH_RATE, refreshRate);
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, resolutionHeight);
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
-            mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
-                device.setUserPreferredDisplayModeLocked(mode);
-            });
+            final int resolutionHeight = mode == null ? Display.INVALID_DISPLAY_HEIGHT
+                    : mode.getPhysicalHeight();
+            final int resolutionWidth = mode == null ? Display.INVALID_DISPLAY_WIDTH
+                    : mode.getPhysicalWidth();
+            final float refreshRate = mode == null ? Display.INVALID_DISPLAY_REFRESH_RATE
+                    : mode.getRefreshRate();
+
+            storeModeInPersistentDataStoreLocked(
+                    displayId, resolutionWidth, resolutionHeight, refreshRate);
+            if (displayId != Display.INVALID_DISPLAY) {
+                setUserPreferredModeForDisplayLocked(displayId, mode);
+            } else {
+                mUserPreferredMode = mode;
+                storeModeInGlobalSettingsLocked(
+                        resolutionWidth, resolutionHeight, refreshRate, mode);
+            }
         }
     }
 
-    private Display.Mode getUserPreferredDisplayModeInternal() {
+    private void storeModeInPersistentDataStoreLocked(int displayId, int resolutionWidth,
+            int resolutionHeight, float refreshRate) {
+        DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId);
+        if (displayDevice == null) {
+            return;
+        }
+        mPersistentDataStore.setUserPreferredResolution(
+                displayDevice, resolutionWidth, resolutionHeight);
+        mPersistentDataStore.setUserPreferredRefreshRate(displayDevice, refreshRate);
+    }
+
+    private void setUserPreferredModeForDisplayLocked(int displayId, Display.Mode mode) {
+        DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId);
+        if (displayDevice == null) {
+            return;
+        }
+        displayDevice.setUserPreferredDisplayModeLocked(mode);
+    }
+
+    private void storeModeInGlobalSettingsLocked(
+            int resolutionWidth, int resolutionHeight, float refreshRate, Display.Mode mode) {
+        Settings.Global.putFloat(mContext.getContentResolver(),
+                Settings.Global.USER_PREFERRED_REFRESH_RATE, refreshRate);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, resolutionHeight);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth);
+        mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
+            device.setUserPreferredDisplayModeLocked(mode);
+        });
+    }
+
+    Display.Mode getUserPreferredDisplayModeInternal(int displayId) {
         synchronized (mSyncRoot) {
-            return mUserPreferredMode;
+            if (displayId == Display.INVALID_DISPLAY) {
+                return mUserPreferredMode;
+            }
+            DisplayDevice displayDevice = getDeviceForDisplayLocked(displayId);
+            if (displayDevice == null) {
+                return null;
+            }
+            return displayDevice.getUserPreferredDisplayModeLocked();
         }
     }
 
@@ -2373,7 +2437,7 @@
             pw.println("  mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
 
             if (mUserPreferredMode != null) {
-                pw.println(mUserPreferredMode.toString());
+                pw.println(mUserPreferredMode);
             }
 
             pw.println();
@@ -3379,23 +3443,23 @@
         }
 
         @Override // Binder call
-        public void setUserPreferredDisplayMode(Display.Mode mode) {
+        public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE,
                     "Permission required to set the user preferred display mode.");
             final long token = Binder.clearCallingIdentity();
             try {
-                setUserPreferredDisplayModeInternal(mode);
+                setUserPreferredDisplayModeInternal(displayId, mode);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override // Binder call
-        public Display.Mode getUserPreferredDisplayMode() {
+        public Display.Mode getUserPreferredDisplayMode(int displayId) {
             final long token = Binder.clearCallingIdentity();
             try {
-                return getUserPreferredDisplayModeInternal();
+                return getUserPreferredDisplayModeInternal(displayId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9a7ddcb..a9a1f08 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -113,13 +113,18 @@
         pw.println("  constrain-launcher-metrics [true|false]");
         pw.println("    Sets if Display#getRealSize and getRealMetrics should be constrained for ");
         pw.println("    Launcher.");
-        pw.println("  set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE");
+        pw.println("  set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE "
+                + "DISPLAY_ID (optional)");
         pw.println("    Sets the user preferred display mode which has fields WIDTH, HEIGHT and "
-                + "REFRESH-RATE");
-        pw.println("  clear-user-preferred-display-mode");
-        pw.println("    Clears the user preferred display mode");
-        pw.println("  get-user-preferred-display-mode");
-        pw.println("    Returns the user preferred display mode or null id no mode is set by user");
+                + "REFRESH-RATE. If DISPLAY_ID is passed, the mode change is applied to display"
+                + "with id = DISPLAY_ID, else mode change is applied globally.");
+        pw.println("  clear-user-preferred-display-mode DISPLAY_ID (optional)");
+        pw.println("    Clears the user preferred display mode. If DISPLAY_ID is passed, the mode"
+                + " is cleared for  display with id = DISPLAY_ID, else mode is cleared globally.");
+        pw.println("  get-user-preferred-display-mode DISPLAY_ID (optional)");
+        pw.println("    Returns the user preferred display mode or null if no mode is set by user."
+                + "If DISPLAY_ID is passed, the mode for display with id = DISPLAY_ID is "
+                + "returned, else global display mode is returned.");
         pw.println("  set-match-content-frame-rate-pref PREFERENCE");
         pw.println("    Sets the match content frame rate preference as PREFERENCE ");
         pw.println("  get-match-content-frame-rate-pref");
@@ -235,28 +240,54 @@
             getErrPrintWriter().println("Error: invalid format of width, height or refresh rate");
             return 1;
         }
-        if (width < 0 || height < 0 || refreshRate <= 0.0f) {
-            getErrPrintWriter().println("Error: invalid value of width, height or refresh rate");
+        if ((width < 0 || height < 0) && refreshRate <= 0.0f) {
+            getErrPrintWriter().println("Error: invalid value of resolution (width, height)"
+                    + " and refresh rate");
             return 1;
         }
 
-        final Context context = mService.getContext();
-        final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        dm.setUserPreferredDisplayMode(new Display.Mode(width, height, refreshRate));
+        final String displayIdText = getNextArg();
+        int displayId = Display.INVALID_DISPLAY;
+        if (displayIdText != null) {
+            try {
+                displayId = Integer.parseInt(displayIdText);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: invalid format of display ID");
+                return 1;
+            }
+        }
+        mService.setUserPreferredDisplayModeInternal(
+                displayId, new Display.Mode(width, height, refreshRate));
         return 0;
     }
 
     private int clearUserPreferredDisplayMode() {
-        final Context context = mService.getContext();
-        final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        dm.clearUserPreferredDisplayMode();
+        final String displayIdText = getNextArg();
+        int displayId = Display.INVALID_DISPLAY;
+        if (displayIdText != null) {
+            try {
+                displayId = Integer.parseInt(displayIdText);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: invalid format of display ID");
+                return 1;
+            }
+        }
+        mService.setUserPreferredDisplayModeInternal(displayId, null);
         return 0;
     }
 
     private int getUserPreferredDisplayMode() {
-        final Context context = mService.getContext();
-        final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        final Display.Mode mode =  dm.getUserPreferredDisplayMode();
+        final String displayIdText = getNextArg();
+        int displayId = Display.INVALID_DISPLAY;
+        if (displayIdText != null) {
+            try {
+                displayId = Integer.parseInt(displayIdText);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: invalid format of display ID");
+                return 1;
+            }
+        }
+        final Display.Mode mode =  mService.getUserPreferredDisplayModeInternal(displayId);
         if (mode == null) {
             getOutPrintWriter().println("User preferred display mode: null");
             return 0;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c4d02c7..34f915e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -62,6 +62,7 @@
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -127,6 +128,7 @@
     private static final int MSG_STOP = 9;
     private static final int MSG_UPDATE_BRIGHTNESS = 10;
     private static final int MSG_UPDATE_RBC = 11;
+    private static final int MSG_STATSD_HBM_BRIGHTNESS = 12;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -136,6 +138,8 @@
     private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
     private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
 
+    private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
+
     // Trigger proximity if distance is less than 5 cm.
     private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
 
@@ -356,6 +360,9 @@
     private float mBrightnessRampRateSlowDecrease;
     private float mBrightnessRampRateSlowIncrease;
 
+    // Report HBM brightness change to StatsD
+    private int mDisplayStatsId;
+    private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN;
 
     // Whether or not to skip the initial brightness ramps into STATE_ON.
     private final boolean mSkipScreenOnBrightnessRamp;
@@ -466,6 +473,7 @@
         TAG = "DisplayPowerController[" + mDisplayId + "]";
         mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+        mDisplayStatsId = mUniqueDisplayId.hashCode();
         mHandler = new DisplayControllerHandler(handler.getLooper());
 
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
@@ -763,6 +771,7 @@
             if (mDisplayDevice != device) {
                 mDisplayDevice = device;
                 mUniqueDisplayId = uniqueId;
+                mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
                 loadFromDisplayDeviceConfig(token, info);
                 updatePowerState();
@@ -816,7 +825,7 @@
         loadNitsRange(mContext.getResources());
         setUpAutoBrightness(mContext.getResources(), mHandler);
         reloadReduceBrightColours();
-        mHbmController.resetHbmData(info.width, info.height, token,
+        mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData());
     }
 
@@ -948,7 +957,9 @@
                     lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
                     darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
                     ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
-                    mHbmController, mIdleModeBrightnessMapper);
+                    mHbmController, mIdleModeBrightnessMapper,
+                    mDisplayDeviceConfig.getAmbientHorizonShort(),
+                    mDisplayDeviceConfig.getAmbientHorizonLong());
         } else {
             mUseSoftwareAutoBrightnessConfig = false;
         }
@@ -1004,7 +1015,15 @@
         }
     };
 
-    private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState;
+    private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
+        @Override
+        public void onAnimationEnd() {
+            sendUpdatePowerState();
+
+            final float brightness = mPowerState.getScreenBrightness();
+            reportStats(brightness);
+        }
+    };
 
     /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
     private void cleanupHandlerThreadAfterStop() {
@@ -1179,6 +1198,13 @@
                     && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                     && Float.isNaN(brightnessState)
                     && mAutomaticBrightnessController != null;
+        final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
+                    && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+        final int autoBrightnessState = autoBrightnessEnabled
+                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
+                : autoBrightnessDisabledDueToDisplayOff
+                ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+                : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
 
@@ -1229,7 +1255,7 @@
         // Configure auto-brightness.
         if (mAutomaticBrightnessController != null) {
             hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
-            mAutomaticBrightnessController.configure(autoBrightnessEnabled,
+            mAutomaticBrightnessController.configure(autoBrightnessState,
                     mBrightnessConfiguration,
                     mLastUserSetScreenBrightness,
                     userSetBrightnessChanged, autoBrightnessAdjustment,
@@ -1626,11 +1652,13 @@
         final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
         final IBinder displayToken =
                 mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
+        final String displayUniqueId =
+                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
         final DisplayDeviceConfig.HighBrightnessModeData hbmData =
                 ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
-                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
+                displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
                 () -> {
                     sendUpdatePowerStateLocked();
                     postBrightnessChangeRunnable();
@@ -2462,6 +2490,42 @@
         }
     }
 
+    private void reportStats(float brightness) {
+        float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
+        synchronized(mCachedBrightnessInfo) {
+            if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
+                return;
+            }
+            hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value;
+        }
+
+        final boolean aboveTransition = brightness > hbmTransitionPoint;
+        final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint;
+
+        if (aboveTransition || oldAboveTransition) {
+            mLastStatsBrightness = brightness;
+            mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS);
+            if (aboveTransition != oldAboveTransition) {
+                // report immediately
+                logHbmBrightnessStats(brightness, mDisplayStatsId);
+            } else {
+                // delay for rate limiting
+                Message msg = mHandler.obtainMessage();
+                msg.what = MSG_STATSD_HBM_BRIGHTNESS;
+                msg.arg1 = Float.floatToIntBits(brightness);
+                msg.arg2 = mDisplayStatsId;
+                mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS);
+            }
+        }
+    }
+
+    private final void logHbmBrightnessStats(float brightness, int displayStatsId) {
+        synchronized (mHandler) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
+        }
+    }
+
     private final class DisplayControllerHandler extends Handler {
         public DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -2526,6 +2590,10 @@
                     final int justActivated = msg.arg2;
                     handleRbcChanged(strengthChanged == 1, justActivated == 1);
                     break;
+
+                case MSG_STATSD_HBM_BRIGHTNESS:
+                    logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 1e1cfeb..16273ce 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,6 +37,7 @@
 import android.view.SurfaceControlHdrLayerInfoListener;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
 import com.android.server.display.DisplayManagerService.Clock;
 
@@ -80,6 +81,7 @@
     private boolean mIsInAllowedAmbientRange = false;
     private boolean mIsTimeAvailable = false;
     private boolean mIsAutoBrightnessEnabled = false;
+    private boolean mIsAutoBrightnessOffByState = false;
     private float mBrightness;
     private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
     private boolean mIsHdrLayerPresent = false;
@@ -88,6 +90,7 @@
     private int mWidth;
     private int mHeight;
     private float mAmbientLux;
+    private int mDisplayStatsId;
 
     /**
      * If HBM is currently running, this is the start time for the current HBM session.
@@ -102,15 +105,15 @@
     private LinkedList<HbmEvent> mEvents = new LinkedList<>();
 
     HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
-            float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
-            Runnable hbmChangeCallback, Context context) {
-        this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax,
-                hbmData, hbmChangeCallback, context);
+            String displayUniqueId, float brightnessMin, float brightnessMax,
+            HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) {
+        this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
+            brightnessMax, hbmData, hbmChangeCallback, context);
     }
 
     @VisibleForTesting
     HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
-            IBinder displayToken, float brightnessMin, float brightnessMax,
+            IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
             HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
             Context context) {
         mInjector = injector;
@@ -126,10 +129,13 @@
         mRecalcRunnable = this::recalculateTimeAllowance;
         mHdrListener = new HdrListener();
 
-        resetHbmData(width, height, displayToken, hbmData);
+        resetHbmData(width, height, displayToken, displayUniqueId, hbmData);
     }
 
-    void setAutoBrightnessEnabled(boolean isEnabled) {
+    void setAutoBrightnessEnabled(int state) {
+        final boolean isEnabled = state == AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+        mIsAutoBrightnessOffByState =
+                state == AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
         if (!deviceSupportsHbm() || isEnabled == mIsAutoBrightnessEnabled) {
             return;
         }
@@ -231,10 +237,12 @@
         mSettingsObserver.stopObserving();
     }
 
-    void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData) {
+    void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
+            HighBrightnessModeData hbmData) {
         mWidth = width;
         mHeight = height;
         mHbmData = hbmData;
+        mDisplayStatsId = displayUniqueId.hashCode();
 
         unregisterHdrListener();
         mSkinThermalStatusObserver.stopObserving();
@@ -275,6 +283,7 @@
                 + (mIsAutoBrightnessEnabled ? "" : " (old/invalid)"));
         pw.println("  mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange);
         pw.println("  mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled);
+        pw.println("  mIsAutoBrightnessOffByState=" + mIsAutoBrightnessOffByState);
         pw.println("  mIsHdrLayerPresent=" + mIsHdrLayerPresent);
         pw.println("  mBrightnessMin=" + mBrightnessMin);
         pw.println("  mBrightnessMax=" + mBrightnessMax);
@@ -436,11 +445,52 @@
     private void updateHbmMode() {
         int newHbmMode = calculateHighBrightnessMode();
         if (mHbmMode != newHbmMode) {
+            updateHbmStats(mHbmMode, newHbmMode);
             mHbmMode = newHbmMode;
             mHbmChangeCallback.run();
         }
     }
 
+    private void updateHbmStats(int mode, int newMode) {
+        int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
+        if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR) {
+            state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR;
+        } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+            state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT;
+        }
+
+        int reason =
+                FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN;
+        boolean oldHbmSv = (mode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+        boolean newHbmSv = (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT);
+        if (oldHbmSv && !newHbmSv) {
+            // If more than one conditions are flipped and turn off HBM sunlight
+            // visibility, only one condition will be reported to make it simple.
+            if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF;
+            } else if (!mIsAutoBrightnessEnabled) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_AUTOBRIGHTNESS_OFF;
+            } else if (!mIsInAllowedAmbientRange) {
+                reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP;
+            } else if (!mIsTimeAvailable) {
+                reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT;
+            } else if (!mIsThermalStatusWithinLimit) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT;
+            } else if (mIsHdrLayerPresent) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING;
+            } else if (mIsBlockedByLowPowerMode) {
+                reason = FrameworkStatsLog
+                                 .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON;
+            }
+        }
+
+        mInjector.reportHbmStateChange(mDisplayStatsId, state, reason);
+    }
+
     private int calculateHighBrightnessMode() {
         if (!deviceSupportsHbm()) {
             return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
@@ -642,5 +692,10 @@
             return IThermalService.Stub.asInterface(
                     ServiceManager.getService(Context.THERMAL_SERVICE));
         }
+
+        public void reportHbmStateChange(int display, int state, int reason) {
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED, display, state, reason);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 300f59e..84de822 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -847,6 +847,10 @@
         public void setUserPreferredDisplayModeLocked(Display.Mode mode) {
             final int oldModeId = getPreferredModeId();
             mUserPreferredMode = mode;
+            if (mode != null && (mode.isRefreshRateSet() ^ mode.isResolutionSet())) {
+                mUserPreferredMode = findMode(mode.getPhysicalWidth(),
+                        mode.getPhysicalHeight(), mode.getRefreshRate());
+            }
             mUserPreferredModeId = findUserPreferredModeIdLocked(mode);
 
             if (oldModeId != getPreferredModeId()) {
@@ -855,6 +859,11 @@
         }
 
         @Override
+        public Display.Mode getUserPreferredDisplayModeLocked() {
+            return mUserPreferredMode;
+        }
+
+        @Override
         public void setRequestedColorModeLocked(int colorMode) {
             requestColorModeLocked(colorMode);
         }
@@ -1062,6 +1071,18 @@
             return matchingModeId;
         }
 
+       // Returns a mode with resolution (width, height) and/or refreshRate. If any one of the
+       // resolution or refresh-rate is valid, a mode having the valid parameters is returned.
+        private Display.Mode findMode(int width, int height, float refreshRate) {
+            for (int i = 0; i < mSupportedModes.size(); i++) {
+                Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode;
+                if (supportedMode.matchesIfValid(width, height, refreshRate)) {
+                    return supportedMode;
+                }
+            }
+            return null;
+        }
+
         private int findUserPreferredModeIdLocked(Display.Mode userPreferredMode) {
             if (userPreferredMode != null) {
                 for (int i = 0; i < mSupportedModes.size(); i++) {
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 4b0d43b..2eba080 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -291,6 +291,54 @@
         return false;
     }
 
+    public boolean setUserPreferredRefreshRate(DisplayDevice displayDevice, float refreshRate) {
+        final String displayDeviceUniqueId = displayDevice.getUniqueId();
+        if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+            return false;
+        }
+        DisplayState state = getDisplayState(displayDevice.getUniqueId(), true);
+        if (state.setRefreshRate(refreshRate)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public float getUserPreferredRefreshRate(DisplayDevice device) {
+        if (device == null || !device.hasStableUniqueId()) {
+            return Float.NaN;
+        }
+        final DisplayState state = getDisplayState(device.getUniqueId(), false);
+        if (state == null) {
+            return Float.NaN;
+        }
+        return state.getRefreshRate();
+    }
+
+    public boolean setUserPreferredResolution(DisplayDevice displayDevice, int width, int height) {
+        final String displayDeviceUniqueId = displayDevice.getUniqueId();
+        if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+            return false;
+        }
+        DisplayState state = getDisplayState(displayDevice.getUniqueId(), true);
+        if (state.setResolution(width, height)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+    public Point getUserPreferredResolution(DisplayDevice displayDevice) {
+        if (displayDevice == null || !displayDevice.hasStableUniqueId()) {
+            return null;
+        }
+        final DisplayState state = getDisplayState(displayDevice.getUniqueId(), false);
+        if (state == null) {
+            return null;
+        }
+        return state.getResolution();
+    }
+
     public Point getStableDisplaySize() {
         loadIfNeeded();
         return mStableDeviceValues.getDisplaySize();
@@ -536,6 +584,9 @@
     private static final class DisplayState {
         private int mColorMode;
         private float mBrightness;
+        private int mWidth;
+        private int mHeight;
+        private float mRefreshRate;
 
         // Brightness configuration by user
         private BrightnessConfigurations mDisplayBrightnessConfigurations =
@@ -576,6 +627,31 @@
             return mDisplayBrightnessConfigurations.mConfigurations.get(userSerial);
         }
 
+        public boolean setResolution(int width, int height) {
+            if (width == mWidth && height == mHeight) {
+                return false;
+            }
+            mWidth = width;
+            mHeight = height;
+            return true;
+        }
+
+        public Point getResolution() {
+            return new Point(mWidth, mHeight);
+        }
+
+        public boolean setRefreshRate(float refreshRate) {
+            if (refreshRate == mRefreshRate) {
+                return false;
+            }
+            mRefreshRate = refreshRate;
+            return true;
+        }
+
+        public float getRefreshRate() {
+            return mRefreshRate;
+        }
+
         public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 58308d8..751f2db 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -81,7 +81,7 @@
     public static final int ADDR_BROADCAST = 15;
 
     /** Logical address used to indicate it is not initialized or invalid. */
-    public static final int ADDR_INVALID = -1;
+    public static final int ADDR_INVALID = HdmiDeviceInfo.ADDR_INVALID;
 
     /** Logical address used to indicate the source comes from internal device. */
     public static final int ADDR_INTERNAL = HdmiDeviceInfo.ADDR_INTERNAL;
@@ -199,6 +199,7 @@
     static final int MESSAGE_SET_SYSTEM_AUDIO_MODE = 0x72;
     static final int MESSAGE_REPORT_AUDIO_STATUS = 0x7A;
     static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D;
+    static final int MESSAGE_SET_AUDIO_VOLUME_LEVEL = 0x73;
     static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 0x7E;
     static final int MESSAGE_ROUTING_CHANGE = 0x80;
     static final int MESSAGE_ROUTING_INFORMATION = 0x81;
@@ -243,7 +244,7 @@
     static final int MESSAGE_CDC_MESSAGE = 0xF8;
     static final int MESSAGE_ABORT = 0xFF;
 
-    static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+    static final int VENDOR_ID_UNKNOWN = HdmiDeviceInfo.VENDOR_ID_UNKNOWN;
 
     static final int TRUE = 1;
     static final int FALSE = 0;
@@ -389,6 +390,17 @@
 
     static final int UNKNOWN_VOLUME = -1;
 
+    // This constant is used in two operands in the CEC spec.
+    //
+    // CEC 1.4: [Audio Volume Status] (part of [Audio Status]) - operand for <Report Audio Status>
+    // Indicates that the current audio volume status is unknown.
+    //
+    // CEC 2.1a: [Audio Volume Level] - operand for <Set Audio Volume Level>
+    // Part of the Absolute Volume Control feature. Indicates that no change shall be made to the
+    // volume level of the recipient. This allows <Set Audio Volume Level> to be sent to determine
+    // whether the recipient supports Absolute Volume Control.
+    static final int AUDIO_VOLUME_STATUS_UNKNOWN = 0x7F;
+
     // States of property PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON
     // to decide if turn on the system audio control when power on the device
     @IntDef({
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 8980de1..e827866 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -77,7 +77,7 @@
 
         private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
         private int mPortId = Constants.INVALID_PORT_ID;
-        private int mVendorId = Constants.UNKNOWN_VENDOR_ID;
+        private int mVendorId = Constants.VENDOR_ID_UNKNOWN;
         private int mPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
         private String mDisplayName = "";
         private int mDeviceType = HdmiDeviceInfo.DEVICE_INACTIVE;
@@ -87,8 +87,15 @@
         }
 
         private HdmiDeviceInfo toHdmiDeviceInfo() {
-            return new HdmiDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType,
-                    mVendorId, mDisplayName, mPowerStatus);
+            return  HdmiDeviceInfo.cecDeviceBuilder()
+                    .setLogicalAddress(mLogicalAddress)
+                    .setPhysicalAddress(mPhysicalAddress)
+                    .setPortId(mPortId)
+                    .setVendorId(mVendorId)
+                    .setDeviceType(mDeviceType)
+                    .setDisplayName(mDisplayName)
+                    .setDevicePowerStatus(mPowerStatus)
+                    .build();
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java
new file mode 100644
index 0000000..4542565
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Sends <Give Features> to a target device. This action succeeds if the device responds with
+ * <Report Features> within {@link HdmiConfig.TIMEOUT_MS}.
+ *
+ * This action does not update the CEC network directly; an incoming <Report Features> message
+ * should be handled separately by {@link HdmiCecNetwork}.
+ */
+public class GiveFeaturesAction extends HdmiCecFeatureAction {
+    private static final String TAG = "GiveFeaturesAction";
+
+    private static final int STATE_WAITING_FOR_REPORT_FEATURES = 1;
+
+    private final int mTargetAddress;
+
+    public GiveFeaturesAction(HdmiCecLocalDevice source, int targetAddress,
+            IHdmiControlCallback callback) {
+        super(source, callback);
+
+        mTargetAddress = targetAddress;
+    }
+
+    boolean start() {
+        sendCommand(HdmiCecMessageBuilder.buildGiveFeatures(getSourceAddress(), mTargetAddress),
+                result -> {
+                    if (result == SendMessageResult.SUCCESS) {
+                        mState = STATE_WAITING_FOR_REPORT_FEATURES;
+                        addTimer(STATE_WAITING_FOR_REPORT_FEATURES, HdmiConfig.TIMEOUT_MS);
+                    } else {
+                        finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+                    }
+                });
+        return true;
+    }
+
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_REPORT_FEATURES) {
+            return false;
+        }
+        if (cmd instanceof ReportFeaturesMessage) {
+            return handleReportFeatures((ReportFeaturesMessage) cmd);
+        }
+        return false;
+    }
+
+    private boolean handleReportFeatures(ReportFeaturesMessage cmd) {
+        if (cmd.getSource() == mTargetAddress) {
+            // No need to update the network, since it should already have processed this message.
+            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+            return true;
+        }
+        return false;
+    }
+
+    void handleTimerEvent(int state) {
+        finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index cc86430..1a568c3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -47,6 +47,7 @@
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
@@ -694,7 +695,19 @@
     @ServiceThreadOnly
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
         assertRunOnServiceThread();
-        HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+
+        if (body.length == 0) {
+            Slog.e(TAG, "Message with empty body received.");
+            return;
+        }
+
+        HdmiCecMessage command = HdmiCecMessage.build(srcAddress, dstAddress, body[0],
+                Arrays.copyOfRange(body, 1, body.length));
+
+        if (command.getValidationResult() != HdmiCecMessageValidator.OK) {
+            Slog.e(TAG, "Invalid message received: " + command);
+        }
+
         HdmiLogger.debug("[R]:" + command);
         addCecMessageToHistory(true /* isReceived */, command);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3aa2f42..c674ffe 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.CallSuper;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -634,7 +635,34 @@
 
     protected abstract List<Integer> getRcFeatures();
 
-    protected abstract List<Integer> getDeviceFeatures();
+    /**
+     * Computes the set of supported device features. To update local state with changes in
+     * the set of supported device features, use {@link #getDeviceFeatures} instead.
+     */
+    protected DeviceFeatures computeDeviceFeatures() {
+        return DeviceFeatures.NO_FEATURES_SUPPORTED;
+    }
+
+    /**
+     * Computes the set of supported device features, and updates local state to match.
+     */
+    private void updateDeviceFeatures() {
+        synchronized (mLock) {
+            setDeviceInfo(getDeviceInfo().toBuilder()
+                    .setDeviceFeatures(computeDeviceFeatures())
+                    .build());
+        }
+    }
+
+    /**
+     * Computes and returns the set of supported device features. Updates local state to match.
+     */
+    protected final DeviceFeatures getDeviceFeatures() {
+        updateDeviceFeatures();
+        synchronized (mLock) {
+            return getDeviceInfo().getDeviceFeatures();
+        }
+    }
 
     @Constants.HandleMessageResult
     protected int handleGiveFeatures(HdmiCecMessage message) {
@@ -655,11 +683,17 @@
 
         int rcProfile = getRcProfile();
         List<Integer> rcFeatures = getRcFeatures();
-        List<Integer> deviceFeatures = getDeviceFeatures();
+        DeviceFeatures deviceFeatures = getDeviceFeatures();
+
+
+        int logicalAddress;
+        synchronized (mLock) {
+            logicalAddress = mDeviceInfo.getLogicalAddress();
+        }
 
         mService.sendCecCommand(
-                HdmiCecMessageBuilder.buildReportFeatures(
-                        mDeviceInfo.getLogicalAddress(),
+                ReportFeaturesMessage.build(
+                        logicalAddress,
                         mService.getCecVersion(),
                         localDeviceTypes,
                         rcProfile,
@@ -922,6 +956,7 @@
     final void handleAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
         mPreferredAddress = logicalAddress;
+        updateDeviceFeatures();
         if (mService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
             reportFeatures();
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 7e71589..2ef3ebf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+
 import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
 import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
 import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
@@ -22,6 +25,7 @@
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -177,14 +181,12 @@
     }
 
     @Override
-    protected List<Integer> getDeviceFeatures() {
-        List<Integer> deviceFeatures = new ArrayList<>();
+    protected DeviceFeatures computeDeviceFeatures() {
+        boolean arcSupport = SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true);
 
-        if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
-            deviceFeatures.add(Constants.DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX);
-        }
-
-        return deviceFeatures;
+        return DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                .setArcRxSupport(arcSupport ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED)
+                .build();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 4f55249..90b4f76 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -28,8 +28,6 @@
 import com.android.server.hdmi.Constants.LocalActivePort;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
-import com.google.android.collect.Lists;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -358,11 +356,6 @@
         return features;
     }
 
-    @Override
-    protected List<Integer> getDeviceFeatures() {
-        return Lists.newArrayList();
-    }
-
     // Active source claiming needs to be handled in Service
     // since service can decide who will be the active source when the device supports
     // multiple device types in this method.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c2ed24a..afcd3dd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,6 +16,8 @@
 
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
 import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_CEC_DISABLE;
 import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION;
 import static android.hardware.hdmi.HdmiControlManager.CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE;
@@ -31,6 +33,7 @@
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
 
 import android.annotation.Nullable;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -346,7 +349,7 @@
                 if (info == null) {
                     // No CEC/MHL device is present at the port. Attempt to switch to
                     // the hardware port itself for non-CEC devices that may be connected.
-                    info = new HdmiDeviceInfo(path, getActivePortId());
+                    info = HdmiDeviceInfo.hardwarePort(path, getActivePortId());
                 }
             }
             mService.invokeInputChangeListener(info);
@@ -1548,9 +1551,7 @@
     }
 
     @Override
-    protected List<Integer> getDeviceFeatures() {
-        List<Integer> deviceFeatures = new ArrayList<>();
-
+    protected DeviceFeatures computeDeviceFeatures() {
         boolean hasArcPort = false;
         List<HdmiPortInfo> ports = mService.getPortInfo();
         for (HdmiPortInfo port : ports) {
@@ -1559,11 +1560,11 @@
                 break;
             }
         }
-        if (hasArcPort) {
-            deviceFeatures.add(Constants.DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX);
-        }
-        deviceFeatures.add(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN);
-        return deviceFeatures;
+
+        return DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                .setRecordTvScreenSupport(FEATURE_SUPPORTED)
+                .setArcTxSupport(hasArcPort ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED)
+                .build();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index e3292a3..290cae5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -16,6 +16,8 @@
 
 package com.android.server.hdmi;
 
+import static com.android.server.hdmi.HdmiCecMessageValidator.ValidationResult;
+
 import android.annotation.Nullable;
 
 import com.android.server.hdmi.Constants.FeatureOpcode;
@@ -26,11 +28,13 @@
 import java.util.Objects;
 
 /**
- * A class to encapsulate HDMI-CEC message used for the devices connected via
- * HDMI cable to communicate with one another. A message is defined by its
- * source and destination address, command (or opcode), and optional parameters.
+ * Encapsulates the data that defines an HDMI-CEC message: source and destination address,
+ * command (or opcode), and optional parameters. Also stores the result of validating the message.
+ *
+ * Subclasses of this class represent specific messages that have been validated, and expose their
+ * parsed parameters.
  */
-public final class HdmiCecMessage {
+public class HdmiCecMessage {
     public static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
 
     private final int mSource;
@@ -39,24 +43,60 @@
     private final int mOpcode;
     private final byte[] mParams;
 
+    private final int mValidationResult;
+
     /**
-     * Constructor.
+     * Constructor that allows the caller to provide the validation result.
+     * Must only be called by subclasses; other callers should use {@link #build}.
      */
-    public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+    protected HdmiCecMessage(int source, int destination, int opcode, byte[] params,
+            @ValidationResult int validationResult) {
         mSource = source;
         mDestination = destination;
         mOpcode = opcode & 0xFF;
         mParams = Arrays.copyOf(params, params.length);
+        mValidationResult = validationResult;
+    }
+
+    private HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+        this(source, destination, opcode, params,
+                HdmiCecMessageValidator.validate(source, destination, opcode & 0xFF, params));
+    }
+
+    /**
+     * Constructs and validates a message. The result of validation will be accessible via
+     * {@link #getValidationResult}.
+     *
+     * Intended for parsing incoming messages, as it takes raw bytes as message parameters.
+     *
+     * If the opcode has its own subclass of this one, this method will instead validate and build
+     * the message using the logic in that class. If successful, it will return a validated
+     * instance of that class that exposes parsed parameters.
+     */
+    static HdmiCecMessage build(int source, int destination, int opcode, byte[] params) {
+        switch (opcode & 0xFF) {
+            case Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL:
+                return SetAudioVolumeLevelMessage.build(source, destination, params);
+            case Constants.MESSAGE_REPORT_FEATURES:
+                return ReportFeaturesMessage.build(source, destination, params);
+            default:
+                return new HdmiCecMessage(source, destination, opcode & 0xFF, params);
+        }
+    }
+
+    static HdmiCecMessage build(int source, int destination, int opcode) {
+        return new HdmiCecMessage(source, destination, opcode, EMPTY_PARAM);
     }
 
     @Override
     public boolean equals(@Nullable Object message) {
         if (message instanceof HdmiCecMessage) {
             HdmiCecMessage that = (HdmiCecMessage) message;
-            return this.mSource == that.getSource() &&
-                this.mDestination == that.getDestination() &&
-                this.mOpcode == that.getOpcode() &&
-                Arrays.equals(this.mParams, that.getParams());
+            return this.mSource == that.getSource()
+                    && this.mDestination == that.getDestination()
+                    && this.mOpcode == that.getOpcode()
+                    && Arrays.equals(this.mParams, that.getParams())
+                    && this.mValidationResult == that.getValidationResult();
         }
         return false;
     }
@@ -111,6 +151,13 @@
         return mParams;
     }
 
+    /**
+     * Returns the validation result of the message.
+     */
+    public int getValidationResult() {
+        return mValidationResult;
+    }
+
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();
@@ -129,9 +176,30 @@
                 }
             }
         }
+        if (mValidationResult != HdmiCecMessageValidator.OK) {
+            s.append(String.format(" <Validation error: %s>",
+                    validationResultToString(mValidationResult)));
+        }
         return s.toString();
     }
 
+    private static String validationResultToString(@ValidationResult int validationResult) {
+        switch (validationResult) {
+            case HdmiCecMessageValidator.OK:
+                return "ok";
+            case HdmiCecMessageValidator.ERROR_SOURCE:
+                return "invalid source";
+            case HdmiCecMessageValidator.ERROR_DESTINATION:
+                return "invalid destination";
+            case HdmiCecMessageValidator.ERROR_PARAMETER:
+                return "invalid parameters";
+            case HdmiCecMessageValidator.ERROR_PARAMETER_SHORT:
+                return "short parameters";
+            default:
+                return "unknown error";
+        }
+    }
+
     private static String opcodeToString(@FeatureOpcode int opcode) {
         switch (opcode) {
             case Constants.MESSAGE_FEATURE_ABORT:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 1c8f21f..adcff4c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -16,17 +16,15 @@
 
 package com.android.server.hdmi;
 
-import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiDeviceInfo;
-
 import com.android.server.hdmi.Constants.AudioCodec;
 
 import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-import java.util.List;
 
 /**
  * A helper class to build {@link HdmiCecMessage} from various cec commands.
+ *
+ * If a message type has its own specific subclass of {@link HdmiCecMessage},
+ * its static factory method is instead declared in that subclass.
  */
 public class HdmiCecMessageBuilder {
     private static final int OSD_NAME_MAX_LENGTH = 14;
@@ -34,20 +32,6 @@
     private HdmiCecMessageBuilder() {}
 
     /**
-     * Build {@link HdmiCecMessage} from raw data.
-     *
-     * @param src source address of command
-     * @param dest destination address of command
-     * @param body body of message. It includes opcode.
-     * @return newly created {@link HdmiCecMessage}
-     */
-    static HdmiCecMessage of(int src, int dest, byte[] body) {
-        byte opcode = body[0];
-        byte params[] = Arrays.copyOfRange(body, 1, body.length);
-        return new HdmiCecMessage(src, dest, opcode, params);
-    }
-
-    /**
      * Build &lt;Feature Abort&gt; command. &lt;Feature Abort&gt; consists of
      * 1 byte original opcode and 1 byte reason fields with basic fields.
      *
@@ -63,7 +47,7 @@
                 (byte) (originalOpcode & 0xFF),
                 (byte) (reason & 0xFF),
         };
-        return buildCommand(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
     }
 
     /**
@@ -74,7 +58,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGivePhysicalAddress(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS);
     }
 
     /**
@@ -85,7 +69,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveOsdNameCommand(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_OSD_NAME);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_OSD_NAME);
     }
 
     /**
@@ -96,7 +80,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID);
     }
 
     /**
@@ -121,7 +105,7 @@
                 (byte) (normalized.charAt(2) & 0xFF),
         };
         // <Set Menu Language> is broadcast message.
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_SET_MENU_LANGUAGE, params);
     }
 
@@ -141,7 +125,7 @@
         } catch (UnsupportedEncodingException e) {
             return null;
         }
-        return buildCommand(src, dest, Constants.MESSAGE_SET_OSD_NAME, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_OSD_NAME, params);
     }
 
     /**
@@ -164,7 +148,7 @@
                 (byte) (deviceType & 0xFF)
         };
         // <Report Physical Address> is broadcast message.
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, params);
     }
 
@@ -185,7 +169,7 @@
                 (byte) (vendorId & 0xFF)
         };
         // <Device Vendor Id> is broadcast message.
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_DEVICE_VENDOR_ID, params);
     }
 
@@ -202,7 +186,7 @@
         byte[] params = new byte[] {
                 (byte) (version & 0xFF)
         };
-        return buildCommand(src, dest, Constants.MESSAGE_CEC_VERSION, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CEC_VERSION, params);
     }
 
     /**
@@ -213,7 +197,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRequestArcInitiation(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION);
     }
 
     /**
@@ -224,7 +208,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildInitiateArc(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_INITIATE_ARC);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_INITIATE_ARC);
     }
 
     /**
@@ -235,7 +219,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildTerminateArc(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_TERMINATE_ARC);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_TERMINATE_ARC);
     }
 
     /**
@@ -246,7 +230,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRequestArcTermination(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION);
     }
 
     /**
@@ -257,7 +241,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildReportArcInitiated(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED);
     }
 
     /**
@@ -268,7 +252,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildReportArcTerminated(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
     }
 
 
@@ -286,7 +270,8 @@
         for (int i = 0; i < params.length ; i++){
             params[i] = (byte) (audioFormats[i] & 0xff);
         }
-        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
+        return HdmiCecMessage.build(
+                src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
     }
 
 
@@ -298,7 +283,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildTextViewOn(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_TEXT_VIEW_ON);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_TEXT_VIEW_ON);
     }
 
     /**
@@ -308,7 +293,8 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRequestActiveSource(int src) {
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
+        return HdmiCecMessage.build(
+                src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
     }
 
     /**
@@ -319,7 +305,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildActiveSource(int src, int physicalAddress) {
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ACTIVE_SOURCE,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ACTIVE_SOURCE,
                 physicalAddressToParam(physicalAddress));
     }
 
@@ -331,7 +317,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildInactiveSource(int src, int physicalAddress) {
-        return buildCommand(src, Constants.ADDR_TV,
+        return HdmiCecMessage.build(src, Constants.ADDR_TV,
                 Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressToParam(physicalAddress));
     }
 
@@ -345,7 +331,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetStreamPath(int src, int streamPath) {
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
                 Constants.MESSAGE_SET_STREAM_PATH, physicalAddressToParam(streamPath));
     }
 
@@ -364,7 +350,7 @@
             (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
             (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
         };
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ROUTING_CHANGE,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ROUTING_CHANGE,
                 param);
     }
 
@@ -378,7 +364,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRoutingInformation(int src, int physicalAddress) {
-        return buildCommand(src, Constants.ADDR_BROADCAST,
+        return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
             Constants.MESSAGE_ROUTING_INFORMATION, physicalAddressToParam(physicalAddress));
     }
 
@@ -390,7 +376,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveDevicePowerStatus(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS);
     }
 
     /**
@@ -405,7 +391,7 @@
         byte[] param = new byte[] {
                 (byte) (powerStatus & 0xFF)
         };
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
     }
 
     /**
@@ -420,7 +406,7 @@
         byte[] param = new byte[] {
                 (byte) (menuStatus & 0xFF)
         };
-        return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_MENU_STATUS, param);
     }
 
     /**
@@ -435,10 +421,10 @@
     static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
             boolean enableSystemAudio) {
         if (enableSystemAudio) {
-            return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+            return HdmiCecMessage.build(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
                     physicalAddressToParam(avrPhysicalAddress));
         } else {
-            return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
+            return HdmiCecMessage.build(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
         }
     }
 
@@ -479,7 +465,8 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildReportShortAudioDescriptor(int src, int des, byte[] sadBytes) {
-        return buildCommand(src, des, Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR, sadBytes);
+        return HdmiCecMessage.build(
+                src, des, Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR, sadBytes);
     }
 
     /**
@@ -490,7 +477,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
     }
 
     /**
@@ -505,7 +492,7 @@
     static HdmiCecMessage buildReportAudioStatus(int src, int dest, int volume, boolean mute) {
         byte status = (byte) ((byte) (mute ? 1 << 7 : 0) | ((byte) volume & 0x7F));
         byte[] params = new byte[] { status };
-        return buildCommand(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
     }
 
     /**
@@ -529,7 +516,8 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) {
-        return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
+        return HdmiCecMessage.build(
+                src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
     }
 
     /**
@@ -540,7 +528,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildUserControlReleased(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
     }
 
     /**
@@ -551,7 +539,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
     }
 
     /**
@@ -562,7 +550,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     public static HdmiCecMessage buildStandby(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_STANDBY);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_STANDBY);
     }
 
     /**
@@ -574,7 +562,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
     }
 
     /**
@@ -593,7 +581,7 @@
         params[1] = (byte) ((vendorId >> 8) & 0xFF);
         params[2] = (byte) (vendorId & 0xFF);
         System.arraycopy(operands, 0, params, 3, operands.length);
-        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
     }
 
     /**
@@ -605,7 +593,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRecordOn(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_RECORD_ON, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_RECORD_ON, params);
     }
 
     /**
@@ -616,7 +604,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildRecordOff(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_RECORD_OFF);
     }
 
     /**
@@ -628,7 +616,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetDigitalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
     }
 
     /**
@@ -640,7 +628,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetAnalogueTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
     }
 
     /**
@@ -652,7 +640,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildSetExternalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
     }
 
     /**
@@ -664,7 +652,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildClearDigitalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
     }
 
     /**
@@ -676,7 +664,7 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildClearAnalogueTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
     }
 
     /**
@@ -688,73 +676,16 @@
      * @return newly created {@link HdmiCecMessage}
      */
     static HdmiCecMessage buildClearExternalTimer(int src, int dest, byte[] params) {
-        return buildCommand(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
     }
 
     static HdmiCecMessage buildGiveFeatures(int src, int dest) {
-        return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES);
-    }
-
-    static HdmiCecMessage buildReportFeatures(int src,
-            @HdmiControlManager.HdmiCecVersion int cecVersion,
-            List<Integer> allDeviceTypes, @Constants.RcProfile int rcProfile,
-            List<Integer> rcFeatures,
-            List<Integer> deviceFeatures) {
-        byte cecVersionByte = (byte) (cecVersion & 0xFF);
-        byte deviceTypes = 0;
-        for (Integer deviceType : allDeviceTypes) {
-            deviceTypes |= 1 << hdmiDeviceInfoDeviceTypeToShiftValue(deviceType);
-        }
-
-        byte rcProfileByte = 0;
-        rcProfileByte |= rcProfile << 6;
-        if (rcProfile == Constants.RC_PROFILE_SOURCE) {
-            for (@Constants.RcProfileSource Integer rcFeature : rcFeatures) {
-                rcProfileByte |= 1 << rcFeature;
-            }
-        } else {
-            @Constants.RcProfileTv byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF);
-            rcProfileByte |= rcProfileTv;
-        }
-
-        byte deviceFeaturesByte = 0;
-        for (@Constants.DeviceFeature Integer deviceFeature : deviceFeatures) {
-            deviceFeaturesByte |= 1 << deviceFeature;
-        }
-
-        byte[] params = {cecVersionByte, deviceTypes, rcProfileByte, deviceFeaturesByte};
-        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REPORT_FEATURES,
-                params);
+        return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_FEATURES);
     }
 
     /***** Please ADD new buildXXX() methods above. ******/
 
     /**
-     * Build a {@link HdmiCecMessage} without extra parameter.
-     *
-     * @param src source address of command
-     * @param dest destination address of command
-     * @param opcode opcode for a message
-     * @return newly created {@link HdmiCecMessage}
-     */
-    private static HdmiCecMessage buildCommand(int src, int dest, int opcode) {
-        return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM);
-    }
-
-    /**
-     * Build a {@link HdmiCecMessage} with given values.
-     *
-     * @param src source address of command
-     * @param dest destination address of command
-     * @param opcode opcode for a message
-     * @param params extra parameters for command
-     * @return newly created {@link HdmiCecMessage}
-     */
-    private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) {
-        return new HdmiCecMessage(src, dest, opcode, params);
-    }
-
-    /**
      * Build a {@link HdmiCecMessage} with a boolean param and other given values.
      *
      * @param src source address of command
@@ -768,7 +699,7 @@
         byte[] params = new byte[]{
             param ? (byte) 0b1 : 0b0
         };
-        return buildCommand(src, des, opcode, params);
+        return HdmiCecMessage.build(src, des, opcode, params);
     }
 
     private static byte[] physicalAddressToParam(int physicalAddress) {
@@ -777,24 +708,4 @@
                 (byte) (physicalAddress & 0xFF)
         };
     }
-
-    @Constants.DeviceType
-    private static int hdmiDeviceInfoDeviceTypeToShiftValue(int deviceType) {
-        switch (deviceType) {
-            case HdmiDeviceInfo.DEVICE_TV:
-                return Constants.ALL_DEVICE_TYPES_TV;
-            case HdmiDeviceInfo.DEVICE_RECORDER:
-                return Constants.ALL_DEVICE_TYPES_RECORDER;
-            case HdmiDeviceInfo.DEVICE_TUNER:
-                return Constants.ALL_DEVICE_TYPES_TUNER;
-            case HdmiDeviceInfo.DEVICE_PLAYBACK:
-                return Constants.ALL_DEVICE_TYPES_PLAYBACK;
-            case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
-                return Constants.ALL_DEVICE_TYPES_AUDIO_SYSTEM;
-            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
-                return Constants.ALL_DEVICE_TYPES_SWITCH;
-            default:
-                throw new IllegalArgumentException("Unhandled device type: " + deviceType);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 8a727c6..220a438 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -16,23 +16,34 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.IntDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.SparseArray;
 
 /**
- * A helper class to validates {@link HdmiCecMessage}.
+ * A helper class to validate {@link HdmiCecMessage}.
+ *
+ * If a message type has its own specific subclass of {@link HdmiCecMessage},
+ * validation is performed in that subclass instead.
  */
 public class HdmiCecMessageValidator {
     private static final String TAG = "HdmiCecMessageValidator";
 
+    @IntDef({
+            OK,
+            ERROR_SOURCE,
+            ERROR_DESTINATION,
+            ERROR_PARAMETER,
+            ERROR_PARAMETER_SHORT,
+    })
+    public @interface ValidationResult {};
+
     static final int OK = 0;
     static final int ERROR_SOURCE = 1;
     static final int ERROR_DESTINATION = 2;
     static final int ERROR_PARAMETER = 3;
     static final int ERROR_PARAMETER_SHORT = 4;
 
-    private final HdmiControlService mService;
-
     interface ParameterValidator {
         /**
          * @return errorCode errorCode can be {@link #OK}, {@link #ERROR_PARAMETER} or
@@ -42,13 +53,13 @@
     }
 
     // Only the direct addressing is allowed.
-    private static final int DEST_DIRECT = 1 << 0;
+    public static final int DEST_DIRECT = 1 << 0;
     // Only the broadcast addressing is allowed.
-    private static final int DEST_BROADCAST = 1 << 1;
+    public static final int DEST_BROADCAST = 1 << 1;
     // Both the direct and the broadcast addressing are allowed.
-    private static final int DEST_ALL = DEST_DIRECT | DEST_BROADCAST;
+    public static final int DEST_ALL = DEST_DIRECT | DEST_BROADCAST;
     // True if the messages from address 15 (unregistered) are allowed.
-    private static final int SRC_UNREGISTERED = 1 << 2;
+    public static final int SRC_UNREGISTERED = 1 << 2;
 
     private static class ValidationInfo {
         public final ParameterValidator parameterValidator;
@@ -60,11 +71,11 @@
         }
     }
 
-    final SparseArray<ValidationInfo> mValidationInfo = new SparseArray<>();
+    private HdmiCecMessageValidator() {}
 
-    public HdmiCecMessageValidator(HdmiControlService service) {
-        mService = service;
+    private static final SparseArray<ValidationInfo> sValidationInfo = new SparseArray<>();
 
+    static {
         // Messages related to the physical address.
         PhysicalAddressValidator physicalAddressValidator = new PhysicalAddressValidator();
         addValidationInfo(Constants.MESSAGE_ACTIVE_SOURCE,
@@ -234,8 +245,6 @@
         // Messages for Feature Discovery.
         addValidationInfo(Constants.MESSAGE_GIVE_FEATURES, noneValidator,
                 DEST_DIRECT | SRC_UNREGISTERED);
-        addValidationInfo(Constants.MESSAGE_REPORT_FEATURES, new VariableLengthValidator(4, 14),
-                DEST_BROADCAST);
 
         // Messages for Dynamic Auto Lipsync
         addValidationInfo(Constants.MESSAGE_REQUEST_CURRENT_LATENCY, physicalAddressValidator,
@@ -250,59 +259,62 @@
                 DEST_BROADCAST | SRC_UNREGISTERED);
     }
 
-    private void addValidationInfo(int opcode, ParameterValidator validator, int addrType) {
-        mValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
+    private static void addValidationInfo(int opcode, ParameterValidator validator, int addrType) {
+        sValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
     }
 
-    int isValid(HdmiCecMessage message, boolean isMessageReceived) {
-        int opcode = message.getOpcode();
-        ValidationInfo info = mValidationInfo.get(opcode);
+    /**
+     * Validates all parameters of a HDMI-CEC message using static information stored in this class.
+     */
+    @ValidationResult
+    static int validate(int source, int destination, int opcode, byte[] params) {
+        ValidationInfo info = sValidationInfo.get(opcode);
+
         if (info == null) {
-            HdmiLogger.warning("No validation information for the message: " + message);
+            HdmiLogger.warning("No validation information for the opcode: " + opcode);
             return OK;
         }
 
+        int addressValidationResult = validateAddress(source, destination, info.addressType);
+        if (addressValidationResult != OK) {
+            return addressValidationResult;
+        }
+
+        // Validate parameters
+        int errorCode = info.parameterValidator.isValid(params);
+        if (errorCode != OK) {
+            return errorCode;
+        }
+
+        return OK;
+    }
+
+    /**
+     * Validates the source and destination addresses of a HDMI-CEC message according to input
+     * address type. Allows address validation logic to be expressed concisely without depending
+     * on static information in this class.
+     * @param source Source address to validate
+     * @param destination Destination address to validate
+     * @param addressType Rules for validating the addresses - e.g. {@link #DEST_BROADCAST}
+     */
+    @ValidationResult
+    static int validateAddress(int source, int destination, int addressType) {
         // Check the source field.
-        if (message.getSource() == Constants.ADDR_UNREGISTERED &&
-                (info.addressType & SRC_UNREGISTERED) == 0) {
-            HdmiLogger.warning("Unexpected source: " + message);
+        if (source == Constants.ADDR_UNREGISTERED
+                && (addressType & SRC_UNREGISTERED) == 0) {
             return ERROR_SOURCE;
         }
 
-        if (isMessageReceived) {
-            // Check if the source's logical address and local device's logical
-            // address are the same.
-            for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
-                synchronized (device.mLock) {
-                    if (message.getSource() == device.getDeviceInfo().getLogicalAddress()
-                            && message.getSource() != Constants.ADDR_UNREGISTERED) {
-                        HdmiLogger.warning(
-                                "Unexpected source: message sent from device itself, " + message);
-                        return ERROR_SOURCE;
-                    }
-                }
-            }
-        }
-
         // Check the destination field.
-        if (message.getDestination() == Constants.ADDR_BROADCAST) {
-            if ((info.addressType & DEST_BROADCAST) == 0) {
-                HdmiLogger.warning("Unexpected broadcast message: " + message);
+        if (destination == Constants.ADDR_BROADCAST) {
+            if ((addressType & DEST_BROADCAST) == 0) {
                 return ERROR_DESTINATION;
             }
         } else {  // Direct addressing.
-            if ((info.addressType & DEST_DIRECT) == 0) {
-                HdmiLogger.warning("Unexpected direct message: " + message);
+            if ((addressType & DEST_DIRECT) == 0) {
                 return ERROR_DESTINATION;
             }
         }
-
-        // Check the parameter type.
-        int errorCode = info.parameterValidator.isValid(message.getParams());
-        if (errorCode != OK) {
-            HdmiLogger.warning("Unexpected parameters: " + message);
-            return errorCode;
-        }
         return OK;
     }
 
@@ -336,7 +348,7 @@
         }
     }
 
-    private boolean isValidPhysicalAddress(byte[] params, int offset) {
+    private static boolean isValidPhysicalAddress(byte[] params, int offset) {
         int physicalAddress = HdmiUtils.twoBytesToInt(params, offset);
         while (physicalAddress != 0) {
             int maskedAddress = physicalAddress & 0xF000;
@@ -345,19 +357,6 @@
                 return false;
             }
         }
-
-        if (!mService.isTvDevice()) {
-            // If the device is not TV, we can't convert path to port-id, so stop here.
-            return true;
-        }
-        int path = HdmiUtils.twoBytesToInt(params, offset);
-        if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == mService.getPhysicalAddress()) {
-            return true;
-        }
-        int portId = mService.pathToPortId(path);
-        if (portId == Constants.INVALID_PORT_ID) {
-            return false;
-        }
         return true;
     }
 
@@ -380,7 +379,7 @@
         return success ? OK : ERROR_PARAMETER;
     }
 
-    private boolean isWithinRange(int value, int min, int max) {
+    private static boolean isWithinRange(int value, int min, int max) {
         value = value & 0xFF;
         return (value >= min && value <= max);
     }
@@ -392,7 +391,7 @@
      * @param value Display Control
      * @return true if the Display Control is valid
      */
-    private boolean isValidDisplayControl(int value) {
+    private static boolean isValidDisplayControl(int value) {
         value = value & 0xFF;
         return (value == 0x00 || value == 0x40 || value == 0x80 || value == 0xC0);
     }
@@ -407,7 +406,7 @@
      * @param maxLength Maximum length of string to be evaluated
      * @return true if the given type is valid
      */
-    private boolean isValidAsciiString(byte[] params, int offset, int maxLength) {
+    private static boolean isValidAsciiString(byte[] params, int offset, int maxLength) {
         for (int i = offset; i < params.length && i < maxLength; i++) {
             if (!isWithinRange(params[i], 0x20, 0x7E)) {
                 return false;
@@ -423,7 +422,7 @@
      * @param value day of month
      * @return true if the day of month is valid
      */
-    private boolean isValidDayOfMonth(int value) {
+    private static boolean isValidDayOfMonth(int value) {
         return isWithinRange(value, 1, 31);
     }
 
@@ -434,7 +433,7 @@
      * @param value month of year
      * @return true if the month of year is valid
      */
-    private boolean isValidMonthOfYear(int value) {
+    private static boolean isValidMonthOfYear(int value) {
         return isWithinRange(value, 1, 12);
     }
 
@@ -445,7 +444,7 @@
      * @param value hour
      * @return true if the hour is valid
      */
-    private boolean isValidHour(int value) {
+    private static boolean isValidHour(int value) {
         return isWithinRange(value, 0, 23);
     }
 
@@ -456,7 +455,7 @@
      * @param value minute
      * @return true if the minute is valid
      */
-    private boolean isValidMinute(int value) {
+    private static boolean isValidMinute(int value) {
         return isWithinRange(value, 0, 59);
     }
 
@@ -467,7 +466,7 @@
      * @param value duration hours
      * @return true if the duration hours is valid
      */
-    private boolean isValidDurationHours(int value) {
+    private static boolean isValidDurationHours(int value) {
         return isWithinRange(value, 0, 99);
     }
 
@@ -478,7 +477,7 @@
      * @param value recording sequence
      * @return true if the given recording sequence is valid
      */
-    private boolean isValidRecordingSequence(int value) {
+    private static boolean isValidRecordingSequence(int value) {
         value = value & 0xFF;
         // Validate bit 7 is set to zero
         if ((value & 0x80) != 0x00) {
@@ -496,7 +495,7 @@
      * @param value analogue broadcast type
      * @return true if the analogue broadcast type is valid
      */
-    private boolean isValidAnalogueBroadcastType(int value) {
+    private static boolean isValidAnalogueBroadcastType(int value) {
         return isWithinRange(value, 0x00, 0x02);
     }
 
@@ -508,7 +507,7 @@
      * @param value analogue frequency
      * @return true if the analogue frequency is valid
      */
-    private boolean isValidAnalogueFrequency(int value) {
+    private static boolean isValidAnalogueFrequency(int value) {
         value = value & 0xFFFF;
         return (value != 0x000 && value != 0xFFFF);
     }
@@ -520,7 +519,7 @@
      * @param value broadcast system
      * @return true if the broadcast system is valid
      */
-    private boolean isValidBroadcastSystem(int value) {
+    private static boolean isValidBroadcastSystem(int value) {
         return isWithinRange(value, 0, 31);
     }
 
@@ -531,7 +530,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is ARIB type
      */
-    private boolean isAribDbs(int value) {
+    private static boolean isAribDbs(int value) {
         return (value == 0x00 || isWithinRange(value, 0x08, 0x0A));
     }
 
@@ -542,7 +541,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is ATSC type
      */
-    private boolean isAtscDbs(int value) {
+    private static boolean isAtscDbs(int value) {
         return (value == 0x01 || isWithinRange(value, 0x10, 0x12));
     }
 
@@ -553,7 +552,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is DVB type
      */
-    private boolean isDvbDbs(int value) {
+    private static boolean isDvbDbs(int value) {
         return (value == 0x02 || isWithinRange(value, 0x18, 0x1B));
     }
 
@@ -565,7 +564,7 @@
      * @param value Digital Broadcast System
      * @return true if the Digital Broadcast System is valid
      */
-    private boolean isValidDigitalBroadcastSystem(int value) {
+    private static boolean isValidDigitalBroadcastSystem(int value) {
         return (isAribDbs(value) || isAtscDbs(value) || isDvbDbs(value));
     }
 
@@ -578,7 +577,7 @@
      * @param offset start offset of Channel Identifier
      * @return true if the Channel Identifier is valid
      */
-    private boolean isValidChannelIdentifier(byte[] params, int offset) {
+    private static boolean isValidChannelIdentifier(byte[] params, int offset) {
         // First 6 bits contain Channel Number Format
         int channelNumberFormat = params[offset] & 0xFC;
         if (channelNumberFormat == 0x04) {
@@ -600,7 +599,7 @@
      * @param offset start offset of Digital Service Identification
      * @return true if the Digital Service Identification is valid
      */
-    private boolean isValidDigitalServiceIdentification(byte[] params, int offset) {
+    private static boolean isValidDigitalServiceIdentification(byte[] params, int offset) {
         // MSB contains Service Identification Method
         int serviceIdentificationMethod = params[offset] & 0x80;
         // Last 7 bits contains Digital Broadcast System
@@ -634,7 +633,7 @@
      * @param value External Plug
      * @return true if the External Plug is valid
      */
-    private boolean isValidExternalPlug(int value) {
+    private static boolean isValidExternalPlug(int value) {
         return isWithinRange(value, 1, 255);
     }
 
@@ -645,7 +644,7 @@
      * @param value External Source Specifier
      * @return true if the External Source is valid
      */
-    private boolean isValidExternalSource(byte[] params, int offset) {
+    private static boolean isValidExternalSource(byte[] params, int offset) {
         int externalSourceSpecifier = params[offset];
         offset = offset + 1;
         if (externalSourceSpecifier == 0x04) {
@@ -661,15 +660,15 @@
         return false;
     }
 
-    private boolean isValidProgrammedInfo(int programedInfo) {
+    private static boolean isValidProgrammedInfo(int programedInfo) {
         return (isWithinRange(programedInfo, 0x00, 0x0B));
     }
 
-    private boolean isValidNotProgrammedErrorInfo(int nonProgramedErrorInfo) {
+    private static boolean isValidNotProgrammedErrorInfo(int nonProgramedErrorInfo) {
         return (isWithinRange(nonProgramedErrorInfo, 0x00, 0x0E));
     }
 
-    private boolean isValidTimerStatusData(byte[] params, int offset) {
+    private static boolean isValidTimerStatusData(byte[] params, int offset) {
         int programedIndicator = params[offset] & 0x10;
         boolean durationAvailable = false;
         if (programedIndicator == 0x10) {
@@ -708,7 +707,7 @@
      * @param value Play mode
      * @return true if the Play mode is valid
      */
-    private boolean isValidPlayMode(int value) {
+    private static boolean isValidPlayMode(int value) {
         return (isWithinRange(value, 0x05, 0x07)
                 || isWithinRange(value, 0x09, 0x0B)
                 || isWithinRange(value, 0x15, 0x17)
@@ -725,7 +724,7 @@
      * @param value UI Broadcast type
      * @return true if the UI Broadcast type is valid
      */
-    private boolean isValidUiBroadcastType(int value) {
+    private static boolean isValidUiBroadcastType(int value) {
         return ((value == 0x00)
                 || (value == 0x01)
                 || (value == 0x10)
@@ -749,7 +748,7 @@
      * @param value UI Sound Presenation Control
      * @return true if the UI Sound Presenation Control is valid
      */
-    private boolean isValidUiSoundPresenationControl(int value) {
+    private static boolean isValidUiSoundPresenationControl(int value) {
         value = value & 0xFF;
         return ((value == 0x20)
                 || (value == 0x30)
@@ -768,7 +767,7 @@
      * @param params Tuner device info
      * @return true if the Tuner device info is valid
      */
-    private boolean isValidTunerDeviceInfo(byte[] params) {
+    private static boolean isValidTunerDeviceInfo(byte[] params) {
         int tunerDisplayInfo = params[0] & 0x7F;
         if (tunerDisplayInfo == 0x00) {
             // Displaying digital tuner
@@ -789,7 +788,7 @@
         return false;
     }
 
-    private class PhysicalAddressValidator implements ParameterValidator {
+    private static class PhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 2) {
@@ -799,7 +798,7 @@
         }
     }
 
-    private class SystemAudioModeRequestValidator extends PhysicalAddressValidator {
+    private static class SystemAudioModeRequestValidator extends PhysicalAddressValidator {
         @Override
         public int isValid(byte[] params) {
             // TV can send <System Audio Mode Request> with no parameters to terminate system audio.
@@ -810,7 +809,7 @@
         }
     }
 
-    private class ReportPhysicalAddressValidator implements ParameterValidator {
+    private static class ReportPhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 3) {
@@ -820,7 +819,7 @@
         }
     }
 
-    private class RoutingChangeValidator implements ParameterValidator {
+    private static class RoutingChangeValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 4) {
@@ -836,7 +835,7 @@
      * A valid parameter should lie within the range description of Record Status Info defined in
      * CEC 1.4 Specification : Operand Descriptions (Section 17)
      */
-    private class RecordStatusInfoValidator implements ParameterValidator {
+    private static class RecordStatusInfoValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -855,7 +854,7 @@
      * A valid parameter should lie within the range description of ASCII defined in CEC 1.4
      * Specification : Operand Descriptions (Section 17)
      */
-    private class AsciiValidator implements ParameterValidator {
+    private static class AsciiValidator implements ParameterValidator {
         private final int mMinLength;
         private final int mMaxLength;
 
@@ -885,7 +884,7 @@
      * A valid parameter should lie within the range description of ASCII defined in CEC 1.4
      * Specification : Operand Descriptions (Section 17)
      */
-    private class OsdStringValidator implements ParameterValidator {
+    private static class OsdStringValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             // If the length is longer than expected, we assume it's OK since the parameter can be
@@ -902,7 +901,7 @@
     }
 
     /** Check if the given parameters are one byte parameters and within range. */
-    private class OneByteRangeValidator implements ParameterValidator {
+    private static class OneByteRangeValidator implements ParameterValidator {
         private final int mMinValue, mMaxValue;
 
         OneByteRangeValidator(int minValue, int maxValue) {
@@ -924,7 +923,7 @@
      * adhere to message description of Analogue Timer defined in CEC 1.4 Specification : Message
      * Descriptions for Timer Programming Feature (CEC Table 12)
      */
-    private class AnalogueTimerValidator implements ParameterValidator {
+    private static class AnalogueTimerValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 11) {
@@ -950,7 +949,7 @@
      * to message description of Digital Timer defined in CEC 1.4 Specification : Message
      * Descriptions for Timer Programming Feature (CEC Table 12)
      */
-    private class DigitalTimerValidator implements ParameterValidator {
+    private static class DigitalTimerValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 11) {
@@ -974,7 +973,7 @@
      * adhere to message description of External Timer defined in CEC 1.4 Specification : Message
      * Descriptions for Timer Programming Feature (CEC Table 12)
      */
-    private class ExternalTimerValidator implements ParameterValidator {
+    private static class ExternalTimerValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 9) {
@@ -997,7 +996,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions
      * (Section 17)
      */
-    private class TimerClearedStatusValidator implements ParameterValidator {
+    private static class TimerClearedStatusValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1011,7 +1010,7 @@
      * Check if the given timer status data parameter is valid. A valid parameter should lie within
      * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
      */
-    private class TimerStatusValidator implements ParameterValidator {
+    private static class TimerStatusValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1025,7 +1024,7 @@
      * Check if the given play mode parameter is valid. A valid parameter should lie within the
      * range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
      */
-    private class PlayModeValidator implements ParameterValidator {
+    private static class PlayModeValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1040,7 +1039,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions
      * (Section 17)
      */
-    private class SelectAnalogueServiceValidator implements ParameterValidator {
+    private static class SelectAnalogueServiceValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 4) {
@@ -1057,7 +1056,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions
      * (Section 17)
      */
-    private class SelectDigitalServiceValidator implements ParameterValidator {
+    private static class SelectDigitalServiceValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 4) {
@@ -1072,7 +1071,7 @@
      * within the range description defined in CEC 1.4 Specification : Operand Descriptions (Section
      * 17)
      */
-    private class TunerDeviceStatusValidator implements ParameterValidator {
+    private static class TunerDeviceStatusValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
@@ -1083,7 +1082,7 @@
     }
 
     /** Check if the given user control press parameter is valid. */
-    private class UserControlPressedValidator implements ParameterValidator {
+    private static class UserControlPressedValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
             if (params.length < 1) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 225785a..6497174 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -57,7 +57,8 @@
  * This class should not take any active action in sending CEC messages.
  *
  * Note that the information cached in this class is not guaranteed to be up-to-date, especially OSD
- * names, power states can be outdated.
+ * names, power states can be outdated. For local devices, more up-to-date information can be
+ * accessed through {@link HdmiCecLocalDevice#getDeviceInfo()}.
  */
 @VisibleForTesting
 public class HdmiCecNetwork {
@@ -390,7 +391,7 @@
             return;
         }
 
-        updateCecDevice(HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus));
+        updateCecDevice(info.toBuilder().setDevicePowerStatus(newPowerStatus).build());
     }
 
     /**
@@ -427,7 +428,8 @@
         for (HdmiPortInfo info : cecPortInfo) {
             portIdMap.put(info.getAddress(), info.getId());
             portInfoMap.put(info.getId(), info);
-            portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
+            portDeviceMap.put(info.getId(),
+                    HdmiDeviceInfo.hardwarePort(info.getAddress(), info.getId()));
         }
         mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
         mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
@@ -496,13 +498,19 @@
         // Add device by logical address if it's not already known
         int sourceAddress = message.getSource();
         if (getCecDeviceInfo(sourceAddress) == null) {
-            HdmiDeviceInfo newDevice = new HdmiDeviceInfo(sourceAddress,
-                    HdmiDeviceInfo.PATH_INVALID, HdmiDeviceInfo.PORT_INVALID,
-                    HdmiDeviceInfo.DEVICE_RESERVED, Constants.UNKNOWN_VENDOR_ID,
-                    HdmiUtils.getDefaultDeviceName(sourceAddress));
+            HdmiDeviceInfo newDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                    .setLogicalAddress(sourceAddress)
+                    .setDisplayName(HdmiUtils.getDefaultDeviceName(sourceAddress))
+                    .build();
             addCecDevice(newDevice);
         }
 
+        // If a message type has its own class, all valid messages of that type
+        // will be represented by an instance of that class.
+        if (message instanceof ReportFeaturesMessage) {
+            handleReportFeatures((ReportFeaturesMessage) message);
+        }
+
         switch (message.getOpcode()) {
             case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
                 handleReportPhysicalAddress(message);
@@ -519,18 +527,20 @@
             case Constants.MESSAGE_CEC_VERSION:
                 handleCecVersion(message);
                 break;
-            case Constants.MESSAGE_REPORT_FEATURES:
-                handleReportFeatures(message);
-                break;
         }
     }
 
     @ServiceThreadOnly
-    private void handleReportFeatures(HdmiCecMessage message) {
+    private void handleReportFeatures(ReportFeaturesMessage message) {
         assertRunOnServiceThread();
 
-        int version = Byte.toUnsignedInt(message.getParams()[0]);
-        updateDeviceCecVersion(message.getSource(), version);
+        HdmiDeviceInfo currentDeviceInfo = getCecDeviceInfo(message.getSource());
+        HdmiDeviceInfo newDeviceInfo = currentDeviceInfo.toBuilder()
+                .setCecVersion(message.getCecVersion())
+                .updateDeviceFeatures(message.getDeviceFeatures())
+                .build();
+
+        updateCecDevice(newDeviceInfo);
     }
 
     @ServiceThreadOnly
@@ -554,11 +564,11 @@
         if (deviceInfo == null) {
             Slog.i(TAG, "Unknown source device info for <Report Physical Address> " + message);
         } else {
-            HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                    physicalAddress,
-                    physicalAddressToPortId(physicalAddress), type, deviceInfo.getVendorId(),
-                    deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus(),
-                    deviceInfo.getCecVersion());
+            HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                    .setPhysicalAddress(physicalAddress)
+                    .setPortId(physicalAddressToPortId(physicalAddress))
+                    .setDeviceType(type)
+                    .build();
             updateCecDevice(updatedDeviceInfo);
         }
     }
@@ -588,11 +598,9 @@
             return;
         }
 
-        HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(), deviceInfo.getDeviceType(),
-                deviceInfo.getVendorId(),
-                deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus(),
-                hdmiCecVersion);
+        HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                .setCecVersion(hdmiCecVersion)
+                .build();
         updateCecDevice(updatedDeviceInfo);
     }
 
@@ -623,10 +631,11 @@
         Slog.d(TAG, "Updating device OSD name from "
                 + deviceInfo.getDisplayName()
                 + " to " + osdName);
-        updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
-                deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName,
-                deviceInfo.getDevicePowerStatus(), deviceInfo.getCecVersion()));
+
+        HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                .setDisplayName(osdName)
+                .build();
+        updateCecDevice(updatedDeviceInfo);
     }
 
     @ServiceThreadOnly
@@ -639,11 +648,9 @@
         if (deviceInfo == null) {
             Slog.i(TAG, "Unknown source device info for <Device Vendor ID> " + message);
         } else {
-            HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
-                    deviceInfo.getPhysicalAddress(),
-                    deviceInfo.getPortId(), deviceInfo.getDeviceType(), vendorId,
-                    deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus(),
-                    deviceInfo.getCecVersion());
+            HdmiDeviceInfo updatedDeviceInfo = deviceInfo.toBuilder()
+                    .setVendorId(vendorId)
+                    .build();
             updateCecDevice(updatedDeviceInfo);
         }
     }
@@ -723,10 +730,7 @@
 
     /**
      * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
-     *
-     *
-     *
-     * qq   * the given routing path. CEC devices use routing path for its physical address to
+     * the given routing path. CEC devices use routing path for its physical address to
      * describe the hierarchy of the devices in the network.
      *
      * @param path routing path or physical address
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 6dd9aa0..8391e0b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -372,8 +372,6 @@
     @Nullable
     private HdmiCecController mCecController;
 
-    private HdmiCecMessageValidator mMessageValidator;
-
     private HdmiCecPowerStatusController mPowerStatusController;
 
     @ServiceThreadOnly
@@ -606,9 +604,6 @@
         mMhlDevices = Collections.emptyList();
 
         mHdmiCecNetwork.initPortInfo();
-        if (mMessageValidator == null) {
-            mMessageValidator = new HdmiCecMessageValidator(this);
-        }
         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                 new HdmiCecConfig.SettingChangeListener() {
                     @Override
@@ -1086,11 +1081,6 @@
     }
 
     @VisibleForTesting
-    void setMessageValidator(HdmiCecMessageValidator messageValidator) {
-        mMessageValidator = messageValidator;
-    }
-
-    @VisibleForTesting
     void setCecMessageBuffer(CecMessageBuffer cecMessageBuffer) {
         this.mCecMessageBuffer = cecMessageBuffer;
     }
@@ -1182,7 +1172,8 @@
     @ServiceThreadOnly
     void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
         assertRunOnServiceThread();
-        if (mMessageValidator.isValid(command, false) == HdmiCecMessageValidator.OK) {
+        if (command.getValidationResult() == HdmiCecMessageValidator.OK
+                && verifyPhysicalAddresses(command)) {
             mCecController.sendCommand(command, callback);
         } else {
             HdmiLogger.error("Invalid message type:" + command);
@@ -1210,20 +1201,99 @@
         mCecController.maySendFeatureAbortCommand(command, reason);
     }
 
+    /**
+     * Returns whether all of the physical addresses in a message could exist in this CEC network.
+     */
+    boolean verifyPhysicalAddresses(HdmiCecMessage message) {
+        byte[] params = message.getParams();
+        switch (message.getOpcode()) {
+            case Constants.MESSAGE_ROUTING_CHANGE:
+                return verifyPhysicalAddress(params, 0)
+                        && verifyPhysicalAddress(params, 2);
+            case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
+                return params.length == 0 || verifyPhysicalAddress(params, 0);
+            case Constants.MESSAGE_ACTIVE_SOURCE:
+            case Constants.MESSAGE_INACTIVE_SOURCE:
+            case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+            case Constants.MESSAGE_ROUTING_INFORMATION:
+            case Constants.MESSAGE_SET_STREAM_PATH:
+                return verifyPhysicalAddress(params, 0);
+            case Constants.MESSAGE_CLEAR_EXTERNAL_TIMER:
+            case Constants.MESSAGE_SET_EXTERNAL_TIMER:
+                return verifyExternalSourcePhysicalAddress(params, 7);
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Returns whether a given physical address could exist in this CEC network.
+     * For a TV, the physical address must either be the address of the TV itself,
+     * or the address of a device connected to one of its ports (possibly indirectly).
+     */
+    private boolean verifyPhysicalAddress(byte[] params, int offset) {
+        if (!isTvDevice()) {
+            // If the device is not TV, we can't convert path to port-id, so stop here.
+            return true;
+        }
+        int path = HdmiUtils.twoBytesToInt(params, offset);
+        if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == getPhysicalAddress()) {
+            return true;
+        }
+        int portId = pathToPortId(path);
+        if (portId == Constants.INVALID_PORT_ID) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether the physical address of an external source could exist in this network.
+     */
+    private boolean verifyExternalSourcePhysicalAddress(byte[] params, int offset) {
+        int externalSourceSpecifier = params[offset];
+        offset = offset + 1;
+        if (externalSourceSpecifier == 0x05) {
+            if (params.length - offset >= 2) {
+                return verifyPhysicalAddress(params, offset);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether the source address of a message is a local logical address.
+     */
+    private boolean sourceAddressIsLocal(HdmiCecMessage message) {
+        for (HdmiCecLocalDevice device : getAllLocalDevices()) {
+            synchronized (device.mLock) {
+                if (message.getSource() == device.getDeviceInfo().getLogicalAddress()
+                        && message.getSource() != Constants.ADDR_UNREGISTERED) {
+                    HdmiLogger.warning(
+                            "Unexpected source: message sent from device itself, " + message);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     @ServiceThreadOnly
     @VisibleForTesting
     @Constants.HandleMessageResult
     protected int handleCecCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        int errorCode = mMessageValidator.isValid(message, true);
-        if (errorCode != HdmiCecMessageValidator.OK) {
-            // We'll not response on the messages with the invalid source or destination
-            // or with parameter length shorter than specified in the standard.
-            if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) {
-                return Constants.ABORT_INVALID_OPERAND;
-            }
+
+        @HdmiCecMessageValidator.ValidationResult
+        int validationResult = message.getValidationResult();
+        if (validationResult == HdmiCecMessageValidator.ERROR_PARAMETER
+                || !verifyPhysicalAddresses(message)) {
+            return Constants.ABORT_INVALID_OPERAND;
+        } else if (validationResult != HdmiCecMessageValidator.OK
+                || sourceAddressIsLocal(message)) {
             return Constants.HANDLED;
         }
+
         getHdmiCecNetwork().handleCecMessage(message);
 
         @Constants.HandleMessageResult int handleMessageResult =
@@ -1409,9 +1479,16 @@
     private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus,
             int cecVersion) {
         String displayName = readStringSetting(Global.DEVICE_NAME, Build.MODEL);
-        return new HdmiDeviceInfo(logicalAddress,
-                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
-                getVendorId(), displayName, powerStatus, cecVersion);
+        return HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(logicalAddress)
+                .setPhysicalAddress(getPhysicalAddress())
+                .setPortId(pathToPortId(getPhysicalAddress()))
+                .setDeviceType(deviceType)
+                .setVendorId(getVendorId())
+                .setDisplayName(displayName)
+                .setDevicePowerStatus(powerStatus)
+                .setCecVersion(cecVersion)
+                .build();
     }
 
     // Set the display name in HdmiDeviceInfo of the current devices to content provided by
@@ -1422,10 +1499,9 @@
             if (deviceInfo.getDisplayName().equals(newDisplayName)) {
                 continue;
             }
-            device.setDeviceInfo(new HdmiDeviceInfo(
-                    deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress(),
-                    deviceInfo.getPortId(), deviceInfo.getDeviceType(), deviceInfo.getVendorId(),
-                    newDisplayName, deviceInfo.getDevicePowerStatus(), deviceInfo.getCecVersion()));
+            synchronized (device.mLock) {
+                device.setDeviceInfo(deviceInfo.toBuilder().setDisplayName(newDisplayName).build());
+            }
             sendCecCommand(
                     HdmiCecMessageBuilder.buildSetOsdNameCommand(
                             deviceInfo.getLogicalAddress(), Constants.ADDR_TV, newDisplayName));
@@ -2629,7 +2705,7 @@
                 return activeSourceInfo;
             }
 
-            return new HdmiDeviceInfo(activeSource.physicalAddress,
+            return HdmiDeviceInfo.hardwarePort(activeSource.physicalAddress,
                     pathToPortId(activeSource.physicalAddress));
         }
 
@@ -2637,7 +2713,7 @@
             int activePath = tv().getActivePath();
             if (activePath != HdmiDeviceInfo.PATH_INVALID) {
                 HdmiDeviceInfo info = mHdmiCecNetwork.getSafeDeviceInfoByPath(activePath);
-                return (info != null) ? info : new HdmiDeviceInfo(activePath,
+                return (info != null) ? info : HdmiDeviceInfo.hardwarePort(activePath,
                         tv().getActivePortId());
             }
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
index 06ecb5a..43469b5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
+++ b/services/core/java/com/android/server/hdmi/HdmiMhlLocalDeviceStub.java
@@ -8,7 +8,7 @@
  */
 final class HdmiMhlLocalDeviceStub {
 
-    private static final HdmiDeviceInfo INFO = new HdmiDeviceInfo(
+    private static final HdmiDeviceInfo INFO = HdmiDeviceInfo.mhlDevice(
             Constants.INVALID_PHYSICAL_ADDRESS, Constants.INVALID_PORT_ID, -1, -1);
     private final HdmiControlService mService;
     private final int mPortId;
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 03e5de8..ba19cf0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -391,15 +391,6 @@
     }
 
     /**
-     * Clone {@link HdmiDeviceInfo} with new power status.
-     */
-    static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) {
-        return new HdmiDeviceInfo(info.getLogicalAddress(),
-                info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
-                info.getVendorId(), info.getDisplayName(), newPowerStatus, info.getCecVersion());
-    }
-
-    /**
      * Dump a {@link SparseArray} to the print writer.
      *
      * <p>The dump is formatted:
@@ -470,7 +461,7 @@
     }
 
     /**
-     * Method to parse target physical address to the port number on the current device.
+     * Method to build target physical address to the port number on the current device.
      *
      * <p>This check assumes target address is valid.
      *
@@ -562,7 +553,28 @@
         for (int i = 0; i < params.length; i++) {
             params[i] = (byte) Integer.parseInt(parts[i + 2], 16);
         }
-        return new HdmiCecMessage(src, dest, opcode, params);
+        return HdmiCecMessage.build(src, dest, opcode, params);
+    }
+
+    /**
+     * Some operands in the CEC spec consist of a variable number of bytes, where each byte except
+     * the last one has bit 7 set to 1.
+     * Given the index of a byte in such an operand, this method returns the index of the last byte
+     * in the operand, or -1 if the input is invalid (e.g. operand not terminated properly).
+     * @param params Byte array representing a CEC message's parameters
+     * @param offset Index of a byte in the operand to find the end of
+     */
+    public static int getEndOfSequence(byte[] params, int offset) {
+        if (offset < 0) {
+            return -1;
+        }
+        while (offset < params.length && ((params[offset] >> 7) & 1) == 1) {
+            offset++;
+        }
+        if (offset >= params.length) {
+            return -1;
+        }
+        return offset;
     }
 
     public static class ShortAudioDescriptorXmlParser {
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index a307ea3..c4b98c2 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -68,7 +68,7 @@
         mDeviceLogicalAddress = deviceLogicalAddress;
         mDevicePhysicalAddress = devicePhysicalAddress;
         mDeviceType = deviceType;
-        mVendorId = Constants.UNKNOWN_VENDOR_ID;
+        mVendorId = Constants.VENDOR_ID_UNKNOWN;
     }
 
     @Override
@@ -174,10 +174,14 @@
         if (mDisplayName == null) {
             mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress);
         }
-        HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(
-                mDeviceLogicalAddress, mDevicePhysicalAddress,
-                tv().getPortId(mDevicePhysicalAddress),
-                mDeviceType, mVendorId, mDisplayName);
+        HdmiDeviceInfo deviceInfo = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mDeviceLogicalAddress)
+                .setPhysicalAddress(mDevicePhysicalAddress)
+                .setPortId(tv().getPortId(mDevicePhysicalAddress))
+                .setDeviceType(mDeviceType)
+                .setVendorId(mVendorId)
+                .setDisplayName(mDisplayName)
+                .build();
         localDevice().mService.getHdmiCecNetwork().addCecDevice(deviceInfo);
 
         // Consume CEC messages we already got for this newly found device.
diff --git a/services/core/java/com/android/server/hdmi/ReportFeaturesMessage.java b/services/core/java/com/android/server/hdmi/ReportFeaturesMessage.java
new file mode 100644
index 0000000..80bfb96
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ReportFeaturesMessage.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ValidationResult;
+
+import android.annotation.NonNull;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Represents a validated <Report Features> message with parsed parameters.
+ *
+ * Only parses the [CEC Version] and [Device Features] operands.
+ * [All Device Types] and [RC Profile] are not parsed, but can be specified in construction.
+ */
+public class ReportFeaturesMessage extends HdmiCecMessage {
+
+    @HdmiControlManager.HdmiCecVersion
+    private final int mCecVersion;
+
+    @NonNull
+    private final DeviceFeatures mDeviceFeatures;
+
+    private ReportFeaturesMessage(int source, int destination, byte[] params, int cecVersion,
+            DeviceFeatures deviceFeatures) {
+        super(source, destination, Constants.MESSAGE_REPORT_FEATURES, params, OK);
+        mCecVersion = cecVersion;
+        mDeviceFeatures = deviceFeatures;
+    }
+
+    /**
+     * Static factory method. Intended for constructing outgoing or test messages, as it uses
+     * structured types instead of raw bytes to construct the parameters.
+     */
+    public static HdmiCecMessage build(
+            int source,
+            @HdmiControlManager.HdmiCecVersion int cecVersion,
+            List<Integer> allDeviceTypes,
+            @Constants.RcProfile int rcProfile,
+            List<Integer> rcFeatures,
+            DeviceFeatures deviceFeatures) {
+
+        byte cecVersionByte = (byte) (cecVersion & 0xFF);
+        byte deviceTypes = 0;
+        for (Integer deviceType : allDeviceTypes) {
+            deviceTypes |= (byte) (1 << hdmiDeviceInfoDeviceTypeToShiftValue(deviceType));
+        }
+
+        byte rcProfileByte = 0;
+        rcProfileByte |= (byte) (rcProfile << 6);
+        if (rcProfile == Constants.RC_PROFILE_SOURCE) {
+            for (@Constants.RcProfileSource Integer rcFeature : rcFeatures) {
+                rcProfileByte |= (byte) (1 << rcFeature);
+            }
+        } else {
+            @Constants.RcProfileTv byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF);
+            rcProfileByte |= rcProfileTv;
+        }
+
+        byte[] fixedOperands = {cecVersionByte, deviceTypes, rcProfileByte};
+        byte[] deviceFeaturesBytes = deviceFeatures.toOperand();
+
+        // Concatenate fixed length operands and [Device Features]
+        byte[] params = Arrays.copyOf(fixedOperands,
+                fixedOperands.length + deviceFeaturesBytes.length);
+        System.arraycopy(deviceFeaturesBytes, 0, params,
+                fixedOperands.length, deviceFeaturesBytes.length);
+
+        @ValidationResult
+        int addressValidationResult = validateAddress(source, Constants.ADDR_BROADCAST);
+        if (addressValidationResult != OK) {
+            return new HdmiCecMessage(source, Constants.ADDR_BROADCAST,
+                    Constants.MESSAGE_REPORT_FEATURES, params, addressValidationResult);
+        } else {
+            return new ReportFeaturesMessage(source, Constants.ADDR_BROADCAST, params,
+                    cecVersion, deviceFeatures);
+        }
+    }
+
+    @Constants.DeviceType
+    private static int hdmiDeviceInfoDeviceTypeToShiftValue(int deviceType) {
+        switch (deviceType) {
+            case HdmiDeviceInfo.DEVICE_TV:
+                return Constants.ALL_DEVICE_TYPES_TV;
+            case HdmiDeviceInfo.DEVICE_RECORDER:
+                return Constants.ALL_DEVICE_TYPES_RECORDER;
+            case HdmiDeviceInfo.DEVICE_TUNER:
+                return Constants.ALL_DEVICE_TYPES_TUNER;
+            case HdmiDeviceInfo.DEVICE_PLAYBACK:
+                return Constants.ALL_DEVICE_TYPES_PLAYBACK;
+            case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
+                return Constants.ALL_DEVICE_TYPES_AUDIO_SYSTEM;
+            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
+                return Constants.ALL_DEVICE_TYPES_SWITCH;
+            default:
+                throw new IllegalArgumentException("Unhandled device type: " + deviceType);
+        }
+    }
+
+    /**
+     * Must only be called from {@link HdmiCecMessage#build}.
+     *
+     * Parses and validates CEC message data as a <Report Features> message. Intended for
+     * constructing a representation of an incoming message, as it takes raw bytes for parameters.
+     *
+     * If successful, returns an instance of {@link ReportFeaturesMessage}.
+     * If unsuccessful, returns an {@link HdmiCecMessage} with the reason for validation failure
+     * accessible through {@link HdmiCecMessage#getValidationResult}.
+     */
+    static HdmiCecMessage build(int source, int destination, byte[] params) {
+        // Helper function for building a message that failed validation
+        Function<Integer, HdmiCecMessage> invalidMessage =
+                validationResult -> new HdmiCecMessage(source, destination,
+                        Constants.MESSAGE_REPORT_FEATURES, params, validationResult);
+
+        @ValidationResult int addressValidationResult = validateAddress(source, destination);
+        if (addressValidationResult != OK) {
+            return invalidMessage.apply(addressValidationResult);
+        }
+
+        if (params.length < 4) {
+            return invalidMessage.apply(ERROR_PARAMETER_SHORT);
+        }
+
+        int cecVersion = Byte.toUnsignedInt(params[0]);
+
+        int rcProfileEnd = HdmiUtils.getEndOfSequence(params, 2);
+        if (rcProfileEnd == -1) {
+            return invalidMessage.apply(ERROR_PARAMETER_SHORT);
+        }
+        int deviceFeaturesEnd = HdmiUtils.getEndOfSequence(
+                params, rcProfileEnd + 1);
+        if (deviceFeaturesEnd == -1) {
+            return invalidMessage.apply(ERROR_PARAMETER_SHORT);
+        }
+        int deviceFeaturesStart = HdmiUtils.getEndOfSequence(params, 2) + 1;
+        byte[] deviceFeaturesBytes = Arrays.copyOfRange(params, deviceFeaturesStart, params.length);
+        DeviceFeatures deviceFeatures = DeviceFeatures.fromOperand(deviceFeaturesBytes);
+
+        return new ReportFeaturesMessage(source, destination, params, cecVersion, deviceFeatures);
+    }
+
+    /**
+     * Validates the source and destination addresses for a <Report Features> message.
+     */
+    public static int validateAddress(int source, int destination) {
+        return HdmiCecMessageValidator.validateAddress(source, destination,
+                HdmiCecMessageValidator.DEST_BROADCAST);
+    }
+
+    /**
+     * Returns the contents of the [CEC Version] operand: the version number of the CEC
+     * specification which was used to design the device.
+     */
+    @HdmiControlManager.HdmiCecVersion
+    public int getCecVersion() {
+        return mCecVersion;
+    }
+
+    /**
+     * Returns the contents of the [Device Features] operand: the set of features supported by
+     * the device.
+     */
+    public DeviceFeatures getDeviceFeatures() {
+        return mDeviceFeatures;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java
new file mode 100644
index 0000000..5154669
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+
+import static com.android.server.hdmi.Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL;
+
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Determines whether a target device supports the <Set Audio Volume Level> message.
+ *
+ * Sends the device <Set Audio Volume Level>[0x7F]. The value 0x7F is defined by the spec such that
+ * setting the volume to this level results in no change to the current volume level.
+ *
+ * The target device supports <Set Audio Volume Level> only if it does not respond with
+ * <Feature Abort> within {@link HdmiConfig.TIMEOUT_MS} milliseconds.
+ */
+public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction {
+    private static final String TAG = "SetAudioVolumeLevelDiscoveryAction";
+
+    private static final int STATE_WAITING_FOR_FEATURE_ABORT = 1;
+
+    private final int mTargetAddress;
+
+    public SetAudioVolumeLevelDiscoveryAction(HdmiCecLocalDevice source,
+            int targetAddress, IHdmiControlCallback callback) {
+        super(source, callback);
+
+        mTargetAddress = targetAddress;
+    }
+
+    boolean start() {
+        sendCommand(SetAudioVolumeLevelMessage.build(
+                getSourceAddress(), mTargetAddress, Constants.AUDIO_VOLUME_STATUS_UNKNOWN),
+                result -> {
+                    if (result == SendMessageResult.SUCCESS) {
+                        // Message sent successfully; wait for <Feature Abort> in response
+                        mState = STATE_WAITING_FOR_FEATURE_ABORT;
+                        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+                    } else {
+                        finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+                    }
+                });
+        return true;
+    }
+
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_FEATURE_ABORT) {
+            return false;
+        }
+        switch (cmd.getOpcode()) {
+            case Constants.MESSAGE_FEATURE_ABORT:
+                return handleFeatureAbort(cmd);
+            default:
+                return false;
+        }
+    }
+
+    private boolean handleFeatureAbort(HdmiCecMessage cmd) {
+        int originalOpcode = cmd.getParams()[0] & 0xFF;
+        if (originalOpcode == MESSAGE_SET_AUDIO_VOLUME_LEVEL && cmd.getSource() == mTargetAddress) {
+            if (updateAvcSupport(FEATURE_NOT_SUPPORTED)) {
+                finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+            } else {
+                finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    void handleTimerEvent(int state) {
+        if (updateAvcSupport(FEATURE_SUPPORTED)) {
+            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+        } else {
+            finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
+        }
+    }
+
+    /**
+     * Updates the System Audio device's support for <Set Audio Volume Level> in the
+     * {@link HdmiCecNetwork}. Can fail if the System Audio device is not in our
+     * {@link HdmiCecNetwork}.
+     *
+     * @return Whether support was successfully updated in the network.
+     */
+    private boolean updateAvcSupport(
+            @DeviceFeatures.FeatureSupportStatus int setAudioVolumeLevelSupport) {
+        HdmiCecNetwork network = source().mService.getHdmiCecNetwork();
+        HdmiDeviceInfo currentDeviceInfo = network.getCecDeviceInfo(mTargetAddress);
+
+        if (currentDeviceInfo == null) {
+            return false;
+        } else {
+            network.updateCecDevice(
+                    currentDeviceInfo.toBuilder()
+                            .setDeviceFeatures(currentDeviceInfo.getDeviceFeatures().toBuilder()
+                                    .setSetAudioVolumeLevelSupport(setAudioVolumeLevelSupport)
+                                    .build())
+                            .build()
+            );
+            return true;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelMessage.java b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelMessage.java
new file mode 100644
index 0000000..2ec0e7f
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelMessage.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ValidationResult;
+
+/**
+ * Represents a validated <Set Audio Volume Level> message with parsed parameters.
+ */
+public class SetAudioVolumeLevelMessage extends HdmiCecMessage {
+    private final int mAudioVolumeLevel;
+
+    private SetAudioVolumeLevelMessage(int source, int destination, byte[] params,
+            int audioVolumeLevel) {
+        super(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, params, OK);
+        mAudioVolumeLevel = audioVolumeLevel;
+    }
+
+    /**
+     * Static factory method. Intended for constructing outgoing or test messages, as it uses
+     * structured types instead of raw bytes to construct the parameters.
+     *
+     * @param source Initiator address. Cannot be {@link Constants#ADDR_UNREGISTERED}
+     * @param destination Destination address. Cannot be {@link Constants#ADDR_BROADCAST}
+     * @param audioVolumeLevel [Audio Volume Level]. Either 0x7F (representing no volume change)
+     *                         or between 0 and 100 inclusive (representing volume percentage).
+     */
+    public static HdmiCecMessage build(int source, int destination, int audioVolumeLevel) {
+        byte[] params = { (byte) (audioVolumeLevel & 0xFF) };
+
+        @ValidationResult
+        int addressValidationResult = validateAddress(source, destination);
+        if (addressValidationResult == OK) {
+            return new SetAudioVolumeLevelMessage(source, destination, params, audioVolumeLevel);
+        } else {
+            return new HdmiCecMessage(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                    params, addressValidationResult);
+        }
+    }
+
+    /**
+     * Must only be called from {@link HdmiCecMessage#build}.
+     *
+     * Parses and validates CEC message data as a <SetAudioVolumeLevel> message. Intended for
+     * constructing a representation of an incoming message, as it takes raw bytes for
+     * parameters.
+     *
+     * If successful, returns an instance of {@link SetAudioVolumeLevelMessage}.
+     * If unsuccessful, returns an {@link HdmiCecMessage} with the reason for validation failure
+     * accessible through {@link HdmiCecMessage#getValidationResult}.
+     */
+    public static HdmiCecMessage build(int source, int destination, byte[] params) {
+        if (params.length == 0) {
+            return new HdmiCecMessage(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                    params, ERROR_PARAMETER_SHORT);
+        }
+
+        int audioVolumeLevel = params[0];
+
+        @ValidationResult
+        int addressValidationResult = validateAddress(source, destination);
+        if (addressValidationResult == OK) {
+            return new SetAudioVolumeLevelMessage(source, destination, params, audioVolumeLevel);
+        } else {
+            return new HdmiCecMessage(source, destination, Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                    params, addressValidationResult);
+        }
+    }
+
+    /**
+     * Validates the source and destination addresses for a <Set Audio Volume Level> message.
+     */
+    public static int validateAddress(int source, int destination) {
+        return HdmiCecMessageValidator.validateAddress(source, destination,
+                HdmiCecMessageValidator.DEST_DIRECT);
+    }
+
+    /**
+     * Returns the contents of the [Audio Volume Level] operand:
+     * either 0x7F, indicating no change to the current volume level,
+     * or a percentage between 0 and 100 (inclusive).
+     */
+    public int getAudioVolumeLevel() {
+        return mAudioVolumeLevel;
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 3af51f4..db13deb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -18,8 +18,6 @@
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
-import static com.android.server.inputmethod.InputMethodManagerService.MSG_INITIALIZE_IME;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -311,11 +309,8 @@
                     if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
                     final InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
                     mSupportsStylusHw = info.supportsStylusHandwriting();
-                    // Dispatch display id for InputMethodService to update context display.
-                    mService.executeOrSendMessage(mCurMethod,
-                            mService.mCaller.obtainMessageIOOO(MSG_INITIALIZE_IME,
-                                    info.getConfigChanges(), mCurMethod, mCurToken,
-                                    mSupportsStylusHw));
+                    mService.executeOrSendInitializeIme(mCurMethod, mCurToken,
+                            info.getConfigChanges(), mSupportsStylusHw);
                     mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
                     mService.reRequestCurrentClientSessionLocked();
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index f1e8d0d..6c68011 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -219,38 +219,38 @@
         int FAILURE = -1;
     }
 
-    static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
-    static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
-    static final int MSG_SHOW_IM_CONFIG = 3;
+    private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
+    private static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
+    private static final int MSG_SHOW_IM_CONFIG = 3;
 
-    static final int MSG_UNBIND_INPUT = 1000;
-    static final int MSG_BIND_INPUT = 1010;
-    static final int MSG_SHOW_SOFT_INPUT = 1020;
-    static final int MSG_HIDE_SOFT_INPUT = 1030;
-    static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
-    static final int MSG_INITIALIZE_IME = 1040;
-    static final int MSG_CREATE_SESSION = 1050;
-    static final int MSG_REMOVE_IME_SURFACE = 1060;
-    static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
-    static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
-    static final int MSG_START_HANDWRITING = 1100;
+    private static final int MSG_UNBIND_INPUT = 1000;
+    private static final int MSG_BIND_INPUT = 1010;
+    private static final int MSG_SHOW_SOFT_INPUT = 1020;
+    private static final int MSG_HIDE_SOFT_INPUT = 1030;
+    private static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
+    private static final int MSG_INITIALIZE_IME = 1040;
+    private static final int MSG_CREATE_SESSION = 1050;
+    private static final int MSG_REMOVE_IME_SURFACE = 1060;
+    private static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
+    private static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
+    private static final int MSG_START_HANDWRITING = 1100;
 
-    static final int MSG_START_INPUT = 2000;
+    private static final int MSG_START_INPUT = 2000;
 
-    static final int MSG_UNBIND_CLIENT = 3000;
-    static final int MSG_BIND_CLIENT = 3010;
-    static final int MSG_SET_ACTIVE = 3020;
-    static final int MSG_SET_INTERACTIVE = 3030;
-    static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
+    private static final int MSG_UNBIND_CLIENT = 3000;
+    private static final int MSG_BIND_CLIENT = 3010;
+    private static final int MSG_SET_ACTIVE = 3020;
+    private static final int MSG_SET_INTERACTIVE = 3030;
+    private static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
 
-    static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
+    private static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
 
-    static final int MSG_SYSTEM_UNLOCK_USER = 5000;
-    static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
+    private static final int MSG_SYSTEM_UNLOCK_USER = 5000;
+    private static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
 
-    static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
+    private static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
 
-    static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
+    private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
 
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
@@ -278,14 +278,14 @@
 
     final Context mContext;
     final Resources mRes;
-    final Handler mHandler;
+    private final Handler mHandler;
     final InputMethodSettings mSettings;
     final SettingsObserver mSettingsObserver;
     final IWindowManager mIWindowManager;
     final WindowManagerInternal mWindowManagerInternal;
     final PackageManagerInternal mPackageManagerInternal;
     final InputManagerInternal mInputManagerInternal;
-    final HandlerCaller mCaller;
+    private final HandlerCaller mCaller;
     final boolean mHasFeature;
     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
             new ArrayMap<>();
@@ -1806,10 +1806,10 @@
                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
                         com.android.internal.R.bool.show_ongoing_ime_switcher);
                 if (mShowOngoingImeSwitcherForPhones) {
-                    final InputMethodMenuController.HardKeyboardListener hardKeyboardListener =
-                            mMenuController.getHardKeyboardListener();
-                    mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
-                            hardKeyboardListener);
+                    mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(available -> {
+                        mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0)
+                                .sendToTarget();
+                    });
                 }
 
                 mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
@@ -2241,7 +2241,7 @@
         }
     }
 
-    void executeOrSendMessage(IInterface target, Message msg) {
+    private void executeOrSendMessage(IInterface target, Message msg) {
          if (target.asBinder() instanceof Binder) {
              mCaller.sendMessage(msg);
          } else {
@@ -2527,6 +2527,13 @@
     }
 
     @AnyThread
+    void executeOrSendInitializeIme(@NonNull IInputMethod inputMethod, @NonNull IBinder token,
+            @android.content.pm.ActivityInfo.Config int configChanges, boolean supportStylusHw) {
+        executeOrSendMessage(inputMethod, mCaller.obtainMessageIOOO(MSG_INITIALIZE_IME,
+                configChanges, inputMethod, token, supportStylusHw));
+    }
+
+    @AnyThread
     void scheduleNotifyImeUidToAudioService(int uid) {
         mCaller.removeMessages(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE);
         mCaller.obtainMessageI(MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE, uid).sendToTarget();
@@ -4416,9 +4423,7 @@
 
             // --------------------------------------------------------------
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
-                final InputMethodMenuController.HardKeyboardListener hardKeyboardListener =
-                        mMenuController.getHardKeyboardListener();
-                hardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
+                mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
                 return true;
             case MSG_SYSTEM_UNLOCK_USER: {
                 final int userId = msg.arg1;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 132be7d..fcb1be0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
-import static com.android.server.inputmethod.InputMethodManagerService.MSG_HARD_KEYBOARD_SWITCH_CHANGED;
 import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
 
 import android.app.ActivityThread;
@@ -28,7 +27,6 @@
 import android.content.DialogInterface;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -62,10 +60,8 @@
     private final InputMethodManagerService mService;
     private final InputMethodUtils.InputMethodSettings mSettings;
     private final InputMethodSubtypeSwitchingController mSwitchingController;
-    private final Handler mHandler;
     private final ArrayMap<String, InputMethodInfo> mMethodMap;
     private final KeyguardManager mKeyguardManager;
-    private final HardKeyboardListener mHardKeyboardListener;
     private final WindowManagerInternal mWindowManagerInternal;
 
     private Context mSettingsContext;
@@ -83,10 +79,8 @@
         mService = service;
         mSettings = mService.mSettings;
         mSwitchingController = mService.mSwitchingController;
-        mHandler = mService.mHandler;
         mMethodMap = mService.mMethodMap;
         mKeyguardManager = mService.mKeyguardManager;
-        mHardKeyboardListener = new HardKeyboardListener();
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
     }
 
@@ -271,10 +265,6 @@
         }
     }
 
-    HardKeyboardListener getHardKeyboardListener() {
-        return mHardKeyboardListener;
-    }
-
     AlertDialog getSwitchingDialogLocked() {
         return mSwitchingDialog;
     }
@@ -290,24 +280,16 @@
         return mSwitchingDialog.isShowing();
     }
 
-    class HardKeyboardListener implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
-        @Override
-        public void onHardKeyboardStatusChange(boolean available) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
-                    available ? 1 : 0));
+    void handleHardKeyboardStatusChange(boolean available) {
+        if (DEBUG) {
+            Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
         }
-
-        public void handleHardKeyboardStatusChange(boolean available) {
-            if (DEBUG) {
-                Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
-            }
-            synchronized (ImfLock.class) {
-                if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
-                        && mSwitchingDialog.isShowing()) {
-                    mSwitchingDialogTitleView.findViewById(
-                            com.android.internal.R.id.hard_keyboard_section).setVisibility(
-                            available ? View.VISIBLE : View.GONE);
-                }
+        synchronized (ImfLock.class) {
+            if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
+                    && mSwitchingDialog.isShowing()) {
+                mSwitchingDialogTitleView.findViewById(
+                        com.android.internal.R.id.hard_keyboard_section).setVisibility(
+                        available ? View.VISIBLE : View.GONE);
             }
         }
     }
diff --git a/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java
new file mode 100644
index 0000000..282e3c1
--- /dev/null
+++ b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static android.os.Process.INVALID_UID;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Holds data used to report the ApplicationLocalesChanged atom.
+ */
+public final class AppLocaleChangedAtomRecord {
+    final int mCallingUid;
+    int mTargetUid = INVALID_UID;
+    String mNewLocales = "";
+    String mPrevLocales = "";
+    int mStatus = FrameworkStatsLog
+            .APPLICATION_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED;
+
+    AppLocaleChangedAtomRecord(int callingUid) {
+        this.mCallingUid = callingUid;
+    }
+
+    void setNewLocales(String newLocales) {
+        this.mNewLocales = newLocales;
+    }
+
+    void setTargetUid(int targetUid) {
+        this.mTargetUid = targetUid;
+    }
+
+    void setPrevLocales(String prevLocales) {
+        this.mPrevLocales = prevLocales;
+    }
+
+    void setStatus(int status) {
+        this.mStatus = status;
+    }
+}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 6aabdb5..1657b22 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -148,40 +149,52 @@
      */
     public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId,
             @NonNull LocaleList locales) throws RemoteException, IllegalArgumentException {
-        requireNonNull(appPackageName);
-        requireNonNull(locales);
-
-        //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
-        userId = mActivityManagerInternal.handleIncomingUser(
-                Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
-                "setApplicationLocales", appPackageName);
-
-        // This function handles two types of set operations:
-        // 1.) A normal, non-privileged app setting its own locale.
-        // 2.) A privileged system service setting locales of another package.
-        // The least privileged case is a normal app performing a set, so check that first and
-        // set locales if the package name is owned by the app. Next, check if the caller has the
-        // necessary permission and set locales.
-        boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId);
-        if (!isCallerOwner) {
-            enforceChangeConfigurationPermission();
-        }
-
-        final long token = Binder.clearCallingIdentity();
+        AppLocaleChangedAtomRecord atomRecordForMetrics = new
+                AppLocaleChangedAtomRecord(Binder.getCallingUid());
         try {
-            setApplicationLocalesUnchecked(appPackageName, userId, locales);
+            requireNonNull(appPackageName);
+            requireNonNull(locales);
+            atomRecordForMetrics.setNewLocales(locales.toLanguageTags());
+            //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
+            userId = mActivityManagerInternal.handleIncomingUser(
+                    Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                    false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
+                    "setApplicationLocales", appPackageName);
+
+            // This function handles two types of set operations:
+            // 1.) A normal, non-privileged app setting its own locale.
+            // 2.) A privileged system service setting locales of another package.
+            // The least privileged case is a normal app performing a set, so check that first and
+            // set locales if the package name is owned by the app. Next, check if the caller has
+            // the necessary permission and set locales.
+            boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId,
+                    atomRecordForMetrics);
+            if (!isCallerOwner) {
+                enforceChangeConfigurationPermission(atomRecordForMetrics);
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setApplicationLocalesUnchecked(appPackageName, userId, locales,
+                        atomRecordForMetrics);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            logMetric(atomRecordForMetrics);
         }
     }
 
     private void setApplicationLocalesUnchecked(@NonNull String appPackageName,
-            @UserIdInt int userId, @NonNull LocaleList locales) {
+            @UserIdInt int userId, @NonNull LocaleList locales,
+            @NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
         if (DEBUG) {
             Slog.d(TAG, "setApplicationLocales: setting locales for package " + appPackageName
                     + " and user " + userId);
         }
+
+        atomRecordForMetrics.setPrevLocales(getApplicationLocalesUnchecked(appPackageName, userId)
+                .toLanguageTags());
         final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
                 mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
                         userId);
@@ -194,6 +207,11 @@
             notifyRegisteredReceivers(appPackageName, userId, locales);
 
             mBackupHelper.notifyBackupManager();
+            atomRecordForMetrics.setStatus(
+                    FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_COMMITTED);
+        } else {
+            atomRecordForMetrics.setStatus(FrameworkStatsLog
+                    .APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_UNCOMMITTED);
         }
     }
 
@@ -259,26 +277,49 @@
     }
 
     /**
+     * Same as {@link LocaleManagerService#isPackageOwnedByCaller(String, int,
+     * AppLocaleChangedAtomRecord)}, but for methods that do not log locale atom.
+     */
+    private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
+        return isPackageOwnedByCaller(appPackageName, userId, /* atomRecordForMetrics= */null);
+    }
+
+    /**
      * Checks if the package is owned by the calling app or not for the given user id.
      *
      * @throws IllegalArgumentException if package not found for given userid
      */
-    private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
+    private boolean isPackageOwnedByCaller(String appPackageName, int userId,
+            @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics) {
         final int uid = mPackageManagerInternal
                 .getPackageUid(appPackageName, /* flags */ 0, userId);
         if (uid < 0) {
             Slog.w(TAG, "Unknown package " + appPackageName + " for user " + userId);
+            if (atomRecordForMetrics != null) {
+                atomRecordForMetrics.setStatus(FrameworkStatsLog
+                        .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE);
+            }
             throw new IllegalArgumentException("Unknown package: " + appPackageName
                     + " for user " + userId);
         }
+        if (atomRecordForMetrics != null) {
+            atomRecordForMetrics.setTargetUid(uid);
+        }
         //Once valid package found, ignore the userId part for validating package ownership
         //as apps with INTERACT_ACROSS_USERS permission could be changing locale for different user.
         return UserHandle.isSameApp(Binder.getCallingUid(), uid);
     }
 
-    private void enforceChangeConfigurationPermission() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
+    private void enforceChangeConfigurationPermission(@NonNull AppLocaleChangedAtomRecord
+            atomRecordForMetrics) {
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
+        } catch (SecurityException e) {
+            atomRecordForMetrics.setStatus(FrameworkStatsLog
+                    .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_PERMISSION_ABSENT);
+            throw e;
+        }
     }
 
     /**
@@ -312,6 +353,7 @@
         }
     }
 
+    @NonNull
     private LocaleList getApplicationLocalesUnchecked(@NonNull String appPackageName,
             @UserIdInt int userId) {
         if (DEBUG) {
@@ -345,4 +387,13 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         // TODO(b/201766221): Implement when there is state.
     }
+
+    private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
+        FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
+                atomRecordForMetrics.mCallingUid,
+                atomRecordForMetrics.mTargetUid,
+                atomRecordForMetrics.mNewLocales,
+                atomRecordForMetrics.mPrevLocales,
+                atomRecordForMetrics.mStatus);
+    }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index fab11a1..682a27a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -39,7 +39,10 @@
 import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
+import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
+import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_WEAK;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -123,6 +126,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.LockscreenCredential;
@@ -135,6 +140,7 @@
 import com.android.server.locksettings.LockSettingsStorage.PersistentData;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.locksettings.SyntheticPasswordManager.TokenType;
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -1123,6 +1129,16 @@
         return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
     }
 
+    private void checkManageWeakEscrowTokenMethodUsage() {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN,
+                "Requires MANAGE_WEAK_ESCROW_TOKEN permission.");
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            throw new IllegalArgumentException(
+                    "Weak escrow token are only for automotive devices.");
+        }
+    }
+
     @Override
     public boolean hasSecureLockScreen() {
         return mHasSecureLockScreen;
@@ -1911,6 +1927,97 @@
         });
     }
 
+    /** Register the given WeakEscrowTokenRemovedListener. */
+    @Override
+    public boolean registerWeakEscrowTokenRemovedListener(
+            @NonNull IWeakEscrowTokenRemovedListener listener) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mSpManager.registerWeakEscrowTokenRemovedListener(listener);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /** Unregister the given WeakEscrowTokenRemovedListener. */
+    @Override
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            @NonNull IWeakEscrowTokenRemovedListener listener) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mSpManager.unregisterWeakEscrowTokenRemovedListener(listener);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public long addWeakEscrowToken(byte[] token, int userId,
+            @NonNull IWeakEscrowTokenActivatedListener listener) {
+        checkManageWeakEscrowTokenMethodUsage();
+        Objects.requireNonNull(listener, "Listener can not be null.");
+        EscrowTokenStateChangeCallback internalListener = (handle, userId1) -> {
+            try {
+                listener.onWeakEscrowTokenActivated(handle, userId1);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Exception while notifying weak escrow token has been activated", e);
+            }
+        };
+        final long restoreToken = Binder.clearCallingIdentity();
+        try {
+            return addEscrowToken(token, TOKEN_TYPE_WEAK, userId, internalListener);
+        } finally {
+            Binder.restoreCallingIdentity(restoreToken);
+        }
+    }
+
+    @Override
+    public boolean removeWeakEscrowToken(long handle, int userId) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return removeEscrowToken(handle, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public boolean isWeakEscrowTokenActive(long handle, int userId) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return isEscrowTokenActive(handle, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public boolean isWeakEscrowTokenValid(long handle, byte[] token, int userId) {
+        checkManageWeakEscrowTokenMethodUsage();
+        final long restoreToken = Binder.clearCallingIdentity();
+        try {
+            synchronized (mSpManager) {
+                if (!mSpManager.hasEscrowData(userId)) {
+                    Slog.w(TAG, "Escrow token is disabled on the current user");
+                    return false;
+                }
+                AuthenticationResult authResult = mSpManager.unwrapWeakTokenBasedSyntheticPassword(
+                        getGateKeeperService(), handle, token, userId);
+                if (authResult.authToken == null) {
+                    Slog.w(TAG, "Invalid escrow token supplied");
+                    return false;
+                }
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(restoreToken);
+        }
+    }
+
     @VisibleForTesting /** Note: this method is overridden in unit tests */
     protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
         if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
@@ -3029,6 +3136,7 @@
     private long setLockCredentialWithAuthTokenLocked(LockscreenCredential credential,
             AuthenticationToken auth, int userId) {
         if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
+        final int savedCredentialType = getCredentialTypeInternal(userId);
         long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
                 credential, auth, userId);
         final Map<Integer, LockscreenCredential> profilePasswords;
@@ -3075,6 +3183,9 @@
 
         setUserPasswordMetrics(credential, userId);
         mManagedProfilePasswordCache.removePassword(userId);
+        if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
+            mSpManager.destroyAllWeakTokenBasedSyntheticPasswords(userId);
+        }
 
         if (profilePasswords != null) {
             for (Map.Entry<Integer, LockscreenCredential> entry : profilePasswords.entrySet()) {
@@ -3242,8 +3353,9 @@
         }
     }
 
-    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback) {
-        if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId);
+    private long addEscrowToken(@NonNull byte[] token, @TokenType int type, int userId,
+            @NonNull EscrowTokenStateChangeCallback callback) {
+        if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId + ", type=" + type);
         synchronized (mSpManager) {
             // Migrate to synthetic password based credentials if the user has no password,
             // the token can then be activated immediately.
@@ -3264,7 +3376,8 @@
                     throw new SecurityException("Escrow token is disabled on the current user");
                 }
             }
-            long handle = mSpManager.createTokenBasedSyntheticPassword(token, userId, callback);
+            long handle = mSpManager.createTokenBasedSyntheticPassword(token, type, userId,
+                    callback);
             if (auth != null) {
                 mSpManager.activateTokenBasedSyntheticPassword(handle, auth, userId);
             }
@@ -3345,8 +3458,8 @@
     private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential,
             long tokenHandle, byte[] token, int userId) {
         final AuthenticationResult result;
-        result = mSpManager.unwrapTokenBasedSyntheticPassword(
-                getGateKeeperService(), tokenHandle, token, userId);
+        result = mSpManager.unwrapTokenBasedSyntheticPassword(getGateKeeperService(), tokenHandle,
+                token, userId);
         if (result.authToken == null) {
             Slog.w(TAG, "Invalid escrow token supplied");
             return false;
@@ -3369,7 +3482,8 @@
         AuthenticationResult authResult;
         synchronized (mSpManager) {
             if (!mSpManager.hasEscrowData(userId)) {
-                throw new SecurityException("Escrow token is disabled on the current user");
+                Slog.w(TAG, "Escrow token is disabled on the current user");
+                return false;
             }
             authResult = mSpManager.unwrapTokenBasedSyntheticPassword(getGateKeeperService(),
                     tokenHandle, token, userId);
@@ -3643,7 +3757,8 @@
         @Override
         public long addEscrowToken(byte[] token, int userId,
                 EscrowTokenStateChangeCallback callback) {
-            return LockSettingsService.this.addEscrowToken(token, userId, callback);
+            return LockSettingsService.this.addEscrowToken(token, TOKEN_TYPE_STRONG, userId,
+                    callback);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 601a572..2da4431 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.PasswordMetrics;
@@ -28,6 +29,7 @@
 import android.hardware.weaver.V1_0.WeaverReadResponse;
 import android.hardware.weaver.V1_0.WeaverReadStatus;
 import android.hardware.weaver.V1_0.WeaverStatus;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.security.GateKeeper;
@@ -41,6 +43,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
@@ -48,6 +51,8 @@
 
 import libcore.util.HexEncoding;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -111,7 +116,8 @@
     private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
     private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
-    private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
+    private static final byte SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED = 1;
+    private static final byte SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED = 2;
 
     // 256-bit synthetic password
     private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
@@ -366,10 +372,47 @@
         }
     }
 
+    static class SyntheticPasswordBlob {
+        byte mVersion;
+        byte mType;
+        byte[] mContent;
+
+        public static SyntheticPasswordBlob create(byte version, byte type, byte[] content) {
+            SyntheticPasswordBlob result = new SyntheticPasswordBlob();
+            result.mVersion = version;
+            result.mType = type;
+            result.mContent = content;
+            return result;
+        }
+
+        public static SyntheticPasswordBlob fromBytes(byte[] data) {
+            SyntheticPasswordBlob result = new SyntheticPasswordBlob();
+            result.mVersion = data[0];
+            result.mType = data[1];
+            result.mContent = Arrays.copyOfRange(data, 2, data.length);
+            return result;
+        }
+
+        public byte[] toByte() {
+            byte[] blob = new byte[mContent.length + 1 + 1];
+            blob[0] = mVersion;
+            blob[1] = mType;
+            System.arraycopy(mContent, 0, blob, 2, mContent.length);
+            return blob;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TOKEN_TYPE_STRONG, TOKEN_TYPE_WEAK})
+    @interface TokenType {}
+    static final int TOKEN_TYPE_STRONG = 0;
+    static final int TOKEN_TYPE_WEAK = 1;
+
     static class TokenData {
         byte[] secdiscardableOnDisk;
         byte[] weaverSecret;
         byte[] aggregatedSecret;
+        @TokenType int mType;
         EscrowTokenStateChangeCallback mCallback;
     }
 
@@ -381,6 +424,9 @@
 
     private final UserManager mUserManager;
 
+    private final RemoteCallbackList<IWeakEscrowTokenRemovedListener> mListeners =
+            new RemoteCallbackList<>();
+
     public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
             UserManager userManager, PasswordSlotManager passwordSlotManager) {
         mContext = context;
@@ -880,13 +926,34 @@
      * Create a token based Synthetic password for the given user.
      * @return the handle of the token
      */
-    public long createTokenBasedSyntheticPassword(byte[] token, int userId,
+    public long createStrongTokenBasedSyntheticPassword(byte[] token, int userId,
+            @Nullable EscrowTokenStateChangeCallback changeCallback) {
+        return createTokenBasedSyntheticPassword(token, TOKEN_TYPE_STRONG, userId,
+                changeCallback);
+    }
+
+    /**
+     * Create a weak token based Synthetic password for the given user.
+     * @return the handle of the weak token
+     */
+    public long createWeakTokenBasedSyntheticPassword(byte[] token, int userId,
+            @Nullable EscrowTokenStateChangeCallback changeCallback) {
+        return createTokenBasedSyntheticPassword(token, TOKEN_TYPE_WEAK, userId,
+                changeCallback);
+    }
+
+    /**
+     * Create a token based Synthetic password of the given type for the given user.
+     * @return the handle of the token
+     */
+    public long createTokenBasedSyntheticPassword(byte[] token, @TokenType int type, int userId,
             @Nullable EscrowTokenStateChangeCallback changeCallback) {
         long handle = generateHandle();
         if (!tokenMap.containsKey(userId)) {
             tokenMap.put(userId, new ArrayMap<>());
         }
         TokenData tokenData = new TokenData();
+        tokenData.mType = type;
         final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
         if (isWeaverAvailable()) {
             tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
@@ -910,6 +977,7 @@
         return new ArraySet<>(tokenMap.get(userId).keySet());
     }
 
+    /** Remove the given pending token. */
     public boolean removePendingToken(long handle, int userId) {
         if (!tokenMap.containsKey(userId)) {
             return false;
@@ -941,7 +1009,7 @@
             mPasswordSlotManager.markSlotInUse(slot);
         }
         saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
-        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
+        createSyntheticPasswordBlob(handle, getTokenBasedBlobType(tokenData.mType), authToken,
                 tokenData.aggregatedSecret, 0L, userId);
         tokenMap.get(userId).remove(handle);
         if (tokenData.mCallback != null) {
@@ -953,26 +1021,23 @@
     private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
             byte[] applicationId, long sid, int userId) {
         final byte[] secret;
-        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
+        if (type == SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED
+                || type == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
             secret = authToken.getEscrowSecret();
         } else {
             secret = authToken.getSyntheticPassword();
         }
         byte[] content = createSPBlob(getKeyName(handle), secret, applicationId, sid);
-        byte[] blob = new byte[content.length + 1 + 1];
         /*
          * We can upgrade from v1 to v2 because that's just a change in the way that
          * the SP is stored. However, we can't upgrade to v3 because that is a change
          * in the way that passwords are derived from the SP.
          */
-        if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
-            blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
-        } else {
-            blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
-        }
-        blob[1] = type;
-        System.arraycopy(content, 0, blob, 2, content.length);
-        saveState(SP_BLOB_NAME, blob, handle, userId);
+        byte version = authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3
+                ? SYNTHETIC_PASSWORD_VERSION_V3 : SYNTHETIC_PASSWORD_VERSION_V2;
+
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob.create(version, type, content);
+        saveState(SP_BLOB_NAME, blob.toByte(), handle, userId);
     }
 
     /**
@@ -1089,6 +1154,36 @@
      */
     public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
             IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob
+                .fromBytes(loadState(SP_BLOB_NAME, handle, userId));
+        return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle,
+                blob.mType, token, userId);
+    }
+
+    /**
+     * Decrypt a synthetic password by supplying an strong escrow token and corresponding token
+     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
+     * verification to referesh the SID & Auth token maintained by the system.
+     */
+    public @NonNull AuthenticationResult unwrapStrongTokenBasedSyntheticPassword(
+            IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
+        return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle,
+                SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED, token, userId);
+    }
+
+    /**
+     * Decrypt a synthetic password by supplying a weak escrow token and corresponding token
+     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
+     * verification to referesh the SID & Auth token maintained by the system.
+     */
+    public @NonNull AuthenticationResult unwrapWeakTokenBasedSyntheticPassword(
+            IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
+        return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle,
+                SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED, token, userId);
+    }
+
+    private  @NonNull AuthenticationResult unwrapTokenBasedSyntheticPasswordInternal(
+            IGateKeeperService gatekeeper, long handle, byte type, byte[] token, int userId) {
         AuthenticationResult result = new AuthenticationResult();
         byte[] secdiscardable = loadSecdiscardable(handle, userId);
         int slotId = loadWeaverSlot(handle, userId);
@@ -1109,8 +1204,7 @@
                     PERSONALISATION_WEAVER_TOKEN, secdiscardable);
         }
         byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
-        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
-                applicationId, 0L, userId);
+        result.authToken = unwrapSyntheticPasswordBlob(handle, type, applicationId, 0L, userId);
         if (result.authToken != null) {
             result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
             if (result.gkResponse == null) {
@@ -1126,33 +1220,33 @@
 
     private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
             byte[] applicationId, long sid, int userId) {
-        byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
-        if (blob == null) {
+        byte[] data = loadState(SP_BLOB_NAME, handle, userId);
+        if (data == null) {
             return null;
         }
-        final byte version = blob[0];
-        if (version != SYNTHETIC_PASSWORD_VERSION_V3
-                && version != SYNTHETIC_PASSWORD_VERSION_V2
-                && version != SYNTHETIC_PASSWORD_VERSION_V1) {
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(data);
+        if (blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V3
+                && blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V2
+                && blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V1) {
             throw new IllegalArgumentException("Unknown blob version");
         }
-        if (blob[1] != type) {
+        if (blob.mType != type) {
             throw new IllegalArgumentException("Invalid blob type");
         }
         final byte[] secret;
-        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
-            secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle),
-                    Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+        if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) {
+            secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle), blob.mContent,
+                    applicationId);
         } else {
-            secret = decryptSPBlob(getKeyName(handle),
-                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+            secret = decryptSPBlob(getKeyName(handle), blob.mContent, applicationId);
         }
         if (secret == null) {
             Slog.e(TAG, "Fail to decrypt SP for user " + userId);
             return null;
         }
-        AuthenticationToken result = new AuthenticationToken(version);
-        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
+        AuthenticationToken result = new AuthenticationToken(blob.mVersion);
+        if (type == SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED
+                || type == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
             if (!loadEscrowData(result, userId)) {
                 Slog.e(TAG, "User is not escrowable: " + userId);
                 return null;
@@ -1161,7 +1255,7 @@
         } else {
             result.recreateDirectly(secret);
         }
-        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
+        if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) {
             Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
             createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
         }
@@ -1233,9 +1327,28 @@
         return hasState(SP_BLOB_NAME, handle, userId);
     }
 
+    /** Destroy the escrow token with the given handle for the given user. */
     public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
+        SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME, handle,
+                userId));
         destroySyntheticPassword(handle, userId);
         destroyState(SECDISCARDABLE_NAME, handle, userId);
+        if (blob.mType == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
+            notifyWeakEscrowTokenRemovedListeners(handle, userId);
+        }
+    }
+
+    /** Destroy all weak escrow tokens for the given user. */
+    public void destroyAllWeakTokenBasedSyntheticPasswords(int userId) {
+        List<Long> handles = mStorage.listSyntheticPasswordHandlesForUser(SECDISCARDABLE_NAME,
+                userId);
+        for (long handle: handles) {
+            SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME,
+                    handle, userId));
+            if (blob.mType == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) {
+                destroyTokenBasedSyntheticPassword(handle, userId);
+            }
+        }
     }
 
     public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
@@ -1285,6 +1398,16 @@
         return loadState(SECDISCARDABLE_NAME, handle, userId);
     }
 
+    private byte getTokenBasedBlobType(@TokenType int type) {
+        switch (type) {
+            case TOKEN_TYPE_WEAK:
+                return SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED;
+            case TOKEN_TYPE_STRONG:
+            default:
+                return SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED;
+        }
+    }
+
     /**
      * Retrieves the saved password metrics associated with a SP handle. Only meaningful to be
      * called on the handle of a password-based synthetic password. A valid AuthenticationToken for
@@ -1439,4 +1562,33 @@
         }
         return success;
     }
+
+    /** Register the given IWeakEscrowTokenRemovedListener. */
+    public boolean registerWeakEscrowTokenRemovedListener(
+            IWeakEscrowTokenRemovedListener listener) {
+        return mListeners.register(listener);
+    }
+
+    /** Unregister the given IWeakEscrowTokenRemovedListener. */
+    public boolean unregisterWeakEscrowTokenRemovedListener(
+            IWeakEscrowTokenRemovedListener listener) {
+        return mListeners.unregister(listener);
+    }
+
+    private void notifyWeakEscrowTokenRemovedListeners(long handle, int userId) {
+        int i = mListeners.beginBroadcast();
+        try {
+            while (i > 0) {
+                i--;
+                try {
+                    mListeners.getBroadcastItem(i).onWeakEscrowTokenRemoved(handle, userId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Exception while notifying WeakEscrowTokenRemovedListener.",
+                            e);
+                }
+            }
+        } finally {
+            mListeners.finishBroadcast();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 03a63b9..8ef42ff 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,11 +16,8 @@
 
 package com.android.server.net;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Network;
-import android.net.NetworkTemplate;
-import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.telephony.SubscriptionPlan;
 
@@ -56,11 +53,6 @@
      */
     public abstract SubscriptionPlan getSubscriptionPlan(Network network);
 
-    /**
-     * Return the active {@link SubscriptionPlan} for the given template.
-     */
-    public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
-
     public static final int QUOTA_TYPE_JOBS = 1;
     public static final int QUOTA_TYPE_MULTIPATH = 2;
 
@@ -99,13 +91,4 @@
      */
     public abstract void setMeteredRestrictedPackagesAsync(
             Set<String> packageNames, int userId);
-
-    /**
-     *  Notifies that the specified {@link NetworkStatsProvider} has reached its quota
-     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
-     *  {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
-     *
-     * @param tag the human readable identifier of the custom network stats provider.
-     */
-    public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag);
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5660951..9bc090f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -151,6 +151,8 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -181,7 +183,6 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.TrafficStats;
@@ -441,7 +442,7 @@
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
-    private NetworkStatsManagerInternal mNetworkStats;
+    private NetworkStatsManager mNetworkStats;
     private final INetworkManagementService mNetworkManager;
     private UsageStatsManagerInternal mUsageStats;
     private AppStandbyInternal mAppStandby;
@@ -453,6 +454,8 @@
     private ConnectivityManager mConnManager;
     private PowerManagerInternal mPowerManagerInternal;
     private PowerWhitelistManager mPowerWhitelistManager;
+    @NonNull
+    private final Dependencies mDeps;
 
     /** Current cached value of the current Battery Saver mode's setting for restrict background. */
     @GuardedBy("mUidRulesFirstLock")
@@ -704,7 +707,7 @@
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement) {
         this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
-                getDefaultClock(), getDefaultSystemDir(), false);
+                getDefaultClock(), getDefaultSystemDir(), false, new Dependencies(context));
     }
 
     private static @NonNull File getDefaultSystemDir() {
@@ -716,9 +719,59 @@
                 Clock.systemUTC());
     }
 
+    static class Dependencies {
+        final Context mContext;
+        final NetworkStatsManager mNetworkStatsManager;
+        Dependencies(Context context) {
+            mContext = context;
+            mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+            // Query stats from NetworkStatsService will trigger a poll by default.
+            // But since NPMS listens stats updated event, and will query stats
+            // after the event. A polling -> updated -> query -> polling loop will be introduced
+            // if polls on open. Hence, while NPMS manages it's poll requests explicitly, set
+            // flag to false to prevent a polling loop.
+            mNetworkStatsManager.setPollOnOpen(false);
+        }
+
+        long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
+            try {
+                final NetworkStats.Bucket ret = mNetworkStatsManager
+                        .querySummaryForDevice(template, start, end);
+                return ret.getRxBytes() + ret.getTxBytes();
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failed to read network stats: " + e);
+                return 0;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_NETWORK);
+            }
+        }
+
+        @NonNull
+        List<NetworkStats.Bucket> getNetworkUidBytes(
+                @NonNull NetworkTemplate template, long start, long end) {
+            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
+            final List<NetworkStats.Bucket> buckets = new ArrayList<>();
+            try {
+                final NetworkStats stats = mNetworkStatsManager.querySummary(template, start, end);
+                while (stats.hasNextBucket()) {
+                    final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+                    stats.getNextBucket(bucket);
+                    buckets.add(bucket);
+                }
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failed to read network stats: " + e);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_NETWORK);
+            }
+            return buckets;
+        }
+    }
+
+    @VisibleForTesting
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
-            File systemDir, boolean suppressDefaultPolicy) {
+            File systemDir, boolean suppressDefaultPolicy, Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing context");
         mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
         mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
@@ -739,10 +792,12 @@
         mUidEventHandler = new Handler(mUidEventThread.getLooper(), mUidEventHandlerCallback);
 
         mSuppressDefaultPolicy = suppressDefaultPolicy;
+        mDeps = Objects.requireNonNull(deps, "missing Dependencies");
 
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"), "net-policy");
 
         mAppOps = context.getSystemService(AppOpsManager.class);
+        mNetworkStats = context.getSystemService(NetworkStatsManager.class);
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
         // Expose private service for system components to use.
         LocalServices.addService(NetworkPolicyManagerInternal.class,
@@ -842,7 +897,6 @@
 
             mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
             mAppStandby = LocalServices.getService(AppStandbyInternal.class);
-            mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
@@ -1161,21 +1215,34 @@
     };
 
     /**
-     * Receiver that watches for {@link INetworkStatsService} updates, which we
+     * Receiver that watches for {@link NetworkStatsManager} updates, which we
      * use to check against {@link NetworkPolicy#warningBytes}.
      */
-    final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() {
+    private final NetworkStatsBroadcastReceiver mStatsReceiver =
+            new NetworkStatsBroadcastReceiver();
+    private class NetworkStatsBroadcastReceiver extends BroadcastReceiver {
+        private boolean mIsAnyIntentReceived = false;
         @Override
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified
             // READ_NETWORK_USAGE_HISTORY permission above.
 
+            mIsAnyIntentReceived = true;
+
             synchronized (mNetworkPoliciesSecondLock) {
                 updateNetworkRulesNL();
                 updateNetworkEnabledNL();
                 updateNotificationsNL();
             }
         }
+
+        /**
+         * Return whether any {@code ACTION_NETWORK_STATS_UPDATED} intent is received.
+         * Used to determine if NetworkStatsService is ready.
+         */
+        public boolean isAnyIntentReceived() {
+            return mIsAnyIntentReceived;
+        }
     };
 
     /**
@@ -1385,15 +1452,17 @@
         long maxBytes = 0;
         int maxUid = 0;
 
-        final NetworkStats stats = getNetworkUidBytes(template, start, end);
-        NetworkStats.Entry entry = null;
-        for (int i = 0; i < stats.size(); i++) {
-            entry = stats.getValues(i, entry);
-            final long bytes = entry.rxBytes + entry.txBytes;
+        // Skip if not ready. NetworkStatsService will block public API calls until it is
+        // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+        if (!mStatsReceiver.isAnyIntentReceived()) return null;
+
+        final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
+        for (final NetworkStats.Bucket entry : stats) {
+            final long bytes = entry.getRxBytes() + entry.getTxBytes();
             totalBytes += bytes;
             if (bytes > maxBytes) {
                 maxBytes = bytes;
-                maxUid = entry.uid;
+                maxUid = entry.getUid();
             }
         }
 
@@ -3363,6 +3432,35 @@
         return result;
     }
 
+    /**
+     * Get subscription plan for the given networkTemplate.
+     *
+     * @param template the networkTemplate to get the subscription plan for.
+     */
+    @Override
+    public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+        synchronized (mNetworkPoliciesSecondLock) {
+            final int subId = findRelevantSubIdNL(template);
+            return getPrimarySubscriptionPlanLocked(subId);
+        }
+    }
+
+    /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+     * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     */
+    @Override
+    public void onStatsProviderWarningOrLimitReached() {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+        // This API may be called before the system is ready.
+        synchronized (mNetworkPoliciesSecondLock) {
+            if (!mSystemReady) return;
+        }
+        mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -5349,25 +5447,10 @@
 
     @Deprecated
     private long getTotalBytes(NetworkTemplate template, long start, long end) {
-        return getNetworkTotalBytes(template, start, end);
-    }
-
-    private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-        try {
-            return mNetworkStats.getNetworkTotalBytes(template, start, end);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failed to read network stats: " + e);
-            return 0;
-        }
-    }
-
-    private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-        try {
-            return mNetworkStats.getNetworkUidBytes(template, start, end);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failed to read network stats: " + e);
-            return new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        }
+        // Skip if not ready. NetworkStatsService will block public API calls until it is
+        // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+        if (!mStatsReceiver.isAnyIntentReceived()) return 0;
+        return mDeps.getNetworkTotalBytes(template, start, end);
     }
 
     private boolean isBandwidthControlEnabled() {
@@ -5583,14 +5666,6 @@
         }
 
         @Override
-        public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
-            synchronized (mNetworkPoliciesSecondLock) {
-                final int subId = findRelevantSubIdNL(template);
-                return getPrimarySubscriptionPlanLocked(subId);
-            }
-        }
-
-        @Override
         public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
             final long quotaBytes;
             synchronized (mNetworkPoliciesSecondLock) {
@@ -5632,12 +5707,6 @@
             mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
                     userId, 0, packageNames).sendToTarget();
         }
-
-        @Override
-        public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
-            Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
-            mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
-        }
     }
 
     private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 399ae53..86b385b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2657,16 +2657,20 @@
     }
 
     private void sendAppBlockStateChangedBroadcast(String pkg, int uid, boolean blocked) {
-        try {
-            getContext().sendBroadcastAsUser(
-                    new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
-                            .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
-                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                            .setPackage(pkg),
-                    UserHandle.of(UserHandle.getUserId(uid)), null);
-        } catch (SecurityException e) {
-            Slog.w(TAG, "Can't notify app about app block change", e);
-        }
+        // From Android T, revoking the notification permission will cause the app to be killed.
+        // delay this broadcast so it doesn't race with that process death
+        mHandler.postDelayed(() -> {
+            try {
+                getContext().sendBroadcastAsUser(
+                        new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
+                                .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
+                                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                                .setPackage(pkg),
+                        UserHandle.of(UserHandle.getUserId(uid)), null);
+            } catch (SecurityException e) {
+                Slog.w(TAG, "Can't notify app about app block change", e);
+            }
+        }, 500);
     }
 
     @Override
@@ -3094,6 +3098,13 @@
                 if (mPreferencesHelper.setValidMessageSent(
                         r.getSbn().getPackageName(), r.getUid())) {
                     handleSavePolicyFile();
+                } else if (r.getNotification().getBubbleMetadata() != null) {
+                    // If bubble metadata is present it is valid (if invalid it's removed
+                    // via BubbleExtractor).
+                    if (mPreferencesHelper.setValidBubbleSent(
+                            r.getSbn().getPackageName(), r.getUid())) {
+                        handleSavePolicyFile();
+                    }
                 }
             } else {
                 if (mPreferencesHelper.setInvalidMessageSent(
@@ -3597,6 +3608,12 @@
         }
 
         @Override
+        public boolean hasSentValidBubble(String pkg, int uid) {
+            checkCallerIsSystem();
+            return mPreferencesHelper.hasSentValidBubble(pkg, uid);
+        }
+
+        @Override
         public void setNotificationDelegate(String callingPkg, String delegate) {
             checkCallerIsSameApp(callingPkg);
             final int callingUid = Binder.getCallingUid();
@@ -6674,31 +6691,33 @@
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
             final int callingUid = Binder.getCallingUid();
-            if (mNotificationsByKey.get(r.getSbn().getKey()) == null
-                    && isCallerInstantApp(callingUid, userId)) {
-                // Ephemeral apps have some special constraints for notifications.
-                // They are not allowed to create new notifications however they are allowed to
-                // update notifications created by the system (e.g. a foreground service
-                // notification).
-                throw new SecurityException("Instant app " + pkg
-                        + " cannot create notifications");
-            }
+            synchronized (mNotificationLock) {
+                if (mNotificationsByKey.get(r.getSbn().getKey()) == null
+                        && isCallerInstantApp(callingUid, userId)) {
+                    // Ephemeral apps have some special constraints for notifications.
+                    // They are not allowed to create new notifications however they are allowed to
+                    // update notifications created by the system (e.g. a foreground service
+                    // notification).
+                    throw new SecurityException("Instant app " + pkg
+                            + " cannot create notifications");
+                }
 
-            // rate limit updates that aren't completed progress notifications
-            if (mNotificationsByKey.get(r.getSbn().getKey()) != null
-                    && !r.getNotification().hasCompletedProgress()
-                    && !isAutogroup) {
+                // rate limit updates that aren't completed progress notifications
+                if (mNotificationsByKey.get(r.getSbn().getKey()) != null
+                        && !r.getNotification().hasCompletedProgress()
+                        && !isAutogroup) {
 
-                final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
-                if (appEnqueueRate > mMaxPackageEnqueueRate) {
-                    mUsageStats.registerOverRateQuota(pkg);
-                    final long now = SystemClock.elapsedRealtime();
-                    if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
-                        Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
-                                + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
-                        mLastOverRateLogTime = now;
+                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
+                        mUsageStats.registerOverRateQuota(pkg);
+                        final long now = SystemClock.elapsedRealtime();
+                        if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+                            Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+                                    + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
+                            mLastOverRateLogTime = now;
+                        }
+                        return false;
                     }
-                    return false;
                 }
             }
 
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 258ae8c..5e333da 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -808,6 +808,23 @@
         }
     }
 
+    /** Sets whether this package has sent a notification with valid bubble metadata. */
+    public boolean setValidBubbleSent(String packageName, int uid) {
+        synchronized (mPackagePreferences) {
+            PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+            boolean valueChanged = !r.hasSentValidBubble;
+            r.hasSentValidBubble = true;
+            return valueChanged;
+        }
+    }
+
+    boolean hasSentValidBubble(String packageName, int uid) {
+        synchronized (mPackagePreferences) {
+            PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+            return r.hasSentValidBubble;
+        }
+    }
+
     @Override
     public boolean isGroupBlocked(String packageName, int uid, String groupId) {
         if (groupId == null) {
@@ -848,6 +865,9 @@
             if (r == null) {
                 throw new IllegalArgumentException("Invalid package");
             }
+            if (fromTargetApp) {
+                group.setBlocked(false);
+            }
             final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
             if (oldGroup != null) {
                 group.setChannels(oldGroup.getChannels());
@@ -2813,8 +2833,9 @@
 
         boolean hasSentInvalidMessage = false;
         boolean hasSentValidMessage = false;
-        // notE: only valid while hasSentMessage is false and hasSentInvalidMessage is true
+        // note: only valid while hasSentMessage is false and hasSentInvalidMessage is true
         boolean userDemotedMsgApp = false;
+        boolean hasSentValidBubble = false;
 
         Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 9302aad..f696b3f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -91,7 +91,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.backup.IBackupManager;
 import android.content.ContentResolver;
@@ -195,38 +194,32 @@
     private final AppDataHelper mAppDataHelper;
     private final BroadcastHelper mBroadcastHelper;
     private final RemovePackageHelper mRemovePackageHelper;
-    private final StorageManager mStorageManager;
-    private final RollbackManagerInternal mRollbackManager;
     private final IncrementalManager mIncrementalManager;
     private final ApexManager mApexManager;
     private final DexManager mDexManager;
     private final ArtManagerService mArtManagerService;
-    private final AppOpsManager mAppOpsManager;
     private final Context mContext;
     private final PackageDexOptimizer mPackageDexOptimizer;
     private final PackageAbiHelper mPackageAbiHelper;
     private final ViewCompiler mViewCompiler;
-    private final IBackupManager mIBackupManager;
     private final SharedLibrariesImpl mSharedLibraries;
+    private final PackageManagerServiceInjector mInjector;
 
     // TODO(b/198166813): remove PMS dependency
     InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
         mPm = pm;
+        mInjector = pm.mInjector;
         mAppDataHelper = appDataHelper;
         mBroadcastHelper = new BroadcastHelper(pm.mInjector);
         mRemovePackageHelper = new RemovePackageHelper(pm);
-        mStorageManager = pm.mInjector.getSystemService(StorageManager.class);
-        mRollbackManager = pm.mInjector.getLocalService(RollbackManagerInternal.class);
         mIncrementalManager = pm.mInjector.getIncrementalManager();
         mApexManager = pm.mInjector.getApexManager();
         mDexManager = pm.mInjector.getDexManager();
         mArtManagerService = pm.mInjector.getArtManagerService();
-        mAppOpsManager = pm.mInjector.getSystemService(AppOpsManager.class);
         mContext = pm.mInjector.getContext();
         mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
         mPackageAbiHelper = pm.mInjector.getAbiHelper();
         mViewCompiler = pm.mInjector.getViewCompiler();
-        mIBackupManager = pm.mInjector.getIBackupManager();
         mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
     }
 
@@ -693,7 +686,8 @@
      * Returns whether the restore successfully completed.
      */
     private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
-        if (mIBackupManager != null) {
+        IBackupManager iBackupManager = mInjector.getIBackupManager();
+        if (iBackupManager != null) {
             // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
             // in the BackupManager. USER_ALL is used in compatibility tests.
             if (userId == UserHandle.USER_ALL) {
@@ -704,8 +698,8 @@
             }
             Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
             try {
-                if (mIBackupManager.isUserReadyForBackup(userId)) {
-                    mIBackupManager.restoreAtInstallForUser(
+                if (iBackupManager.isUserReadyForBackup(userId)) {
+                    iBackupManager.restoreAtInstallForUser(
                             userId, res.mPkg.getPackageName(), token);
                 } else {
                     Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
@@ -756,7 +750,9 @@
 
         if (ps != null && doSnapshotOrRestore) {
             final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
-            mRollbackManager.snapshotAndRestoreUserData(packageName,
+            final RollbackManagerInternal rollbackManager =
+                    mInjector.getLocalService(RollbackManagerInternal.class);
+            rollbackManager.snapshotAndRestoreUserData(packageName,
                     UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token);
             return true;
         }
@@ -1969,14 +1965,15 @@
                             reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
                     if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
                             == 0) {
-                        if (ps1.getOldCodePaths() == null) {
-                            ps1.setOldCodePaths(new ArraySet<>());
+                        Set<String> oldCodePaths = ps1.getOldCodePaths();
+                        if (oldCodePaths == null) {
+                            oldCodePaths = new ArraySet<>();
                         }
-                        Collections.addAll(ps1.getOldCodePaths(), oldPackage.getBaseApkPath());
+                        Collections.addAll(oldCodePaths, oldPackage.getBaseApkPath());
                         if (oldPackage.getSplitCodePaths() != null) {
-                            Collections.addAll(ps1.getOldCodePaths(),
-                                    oldPackage.getSplitCodePaths());
+                            Collections.addAll(oldCodePaths, oldPackage.getSplitCodePaths());
                         }
+                        ps1.setOldCodePaths(oldCodePaths);
                     } else {
                         ps1.setOldCodePaths(null);
                     }
@@ -2787,8 +2784,10 @@
                 // Send broadcast package appeared if external for all users
                 if (res.mPkg.isExternalStorage()) {
                     if (!update) {
+                        final StorageManager storageManager =
+                                mInjector.getSystemService(StorageManager.class);
                         VolumeInfo volume =
-                                mStorageManager.findVolumeByUuid(
+                                storageManager.findVolumeByUuid(
                                         StorageManager.convert(
                                                 res.mPkg.getVolumeUuid()).toString());
                         int packageExternalStorageType =
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 8ebd254..b3bb26c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -513,19 +513,25 @@
             if (!valid) {
                 Slog.w(TAG, "Remove old session: " + session.sessionId);
                 // Remove expired sessions as well as child sessions if any
-                mSessions.remove(session.sessionId);
-                // Since this is early during boot we don't send
-                // any observer events about the session, but we
-                // keep details around for dumpsys.
-                addHistoricalSessionLocked(session);
-                for (PackageInstallerSession child : session.getChildSessions()) {
-                    mSessions.remove(child.sessionId);
-                    addHistoricalSessionLocked(child);
-                }
+                removeActiveSession(session);
             }
         }
     }
 
+    /**
+     * Moves a session (including the child sessions) from mSessions to mHistoricalSessions.
+     * This should only be called on a root session.
+     */
+    @GuardedBy("mSessions")
+    private void removeActiveSession(PackageInstallerSession session) {
+        mSessions.remove(session.sessionId);
+        addHistoricalSessionLocked(session);
+        for (PackageInstallerSession child : session.getChildSessions()) {
+            mSessions.remove(child.sessionId);
+            addHistoricalSessionLocked(child);
+        }
+    }
+
     @GuardedBy("mSessions")
     private void addHistoricalSessionLocked(PackageInstallerSession session) {
         CharArrayWriter writer = new CharArrayWriter();
@@ -1654,10 +1660,11 @@
                         mStagingManager.abortSession(session.mStagedSession);
                     }
                     synchronized (mSessions) {
-                        if (!session.isStaged() || !success) {
-                            mSessions.remove(session.sessionId);
+                        // Child sessions will be removed along with its parent as a whole
+                        if (!session.hasParentSessionId()
+                                && (!session.isStaged() || session.isDestroyed())) {
+                            removeActiveSession(session);
                         }
-                        addHistoricalSessionLocked(session);
 
                         final File appIconFile = buildAppIconFile(session.sessionId);
                         if (appIconFile.exists()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 304ad72..c813317 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -27,6 +27,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -2097,10 +2098,10 @@
         } else {
             // Session is sealed and committed but could not be verified, we need to destroy it.
             destroy();
-            // Dispatch message to remove session from PackageInstallerService.
-            dispatchSessionFinished(error, msg, null);
-            maybeFinishChildSessions(error, msg);
         }
+        // Dispatch message to remove session from PackageInstallerService.
+        dispatchSessionFinished(error, msg, null);
+        maybeFinishChildSessions(error, msg);
     }
 
     private void onSessionInstallationFailure(int error, String detailedMessage) {
@@ -2301,7 +2302,7 @@
         if (params.isStaged) {
             // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
             //  though ideally, we just need to send session committed broadcast.
-            dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
+            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
 
             mStagedSession.verifySession();
         } else {
@@ -2549,6 +2550,9 @@
                 mStagedSession.notifyEndPreRebootVerification();
                 if (error == SessionInfo.SESSION_NO_ERROR) {
                     mStagingManager.commitSession(mStagedSession);
+                } else {
+                    dispatchSessionFinished(INSTALL_FAILED_VERIFICATION_FAILURE, msg, null);
+                    maybeFinishChildSessions(INSTALL_FAILED_VERIFICATION_FAILURE, msg);
                 }
             });
             return;
@@ -2578,7 +2582,7 @@
         // Do not try to install staged apex session. Parent session will have at least one apk
         // session.
         if (!isMultiPackage() && isApexSession() && params.isStaged) {
-            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
+            dispatchSessionFinished(INSTALL_SUCCEEDED,
                     "Apex package should have been installed by apexd", null);
             return null;
         }
@@ -2592,14 +2596,12 @@
             @Override
             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                     Bundle extras) {
-                if (isStaged()) {
-                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
-                } else {
+                if (!isStaged()) {
                     // We've reached point of no return; call into PMS to install the stage.
                     // Regardless of success or failure we always destroy session.
                     destroyInternal();
-                    dispatchSessionFinished(returnCode, msg, extras);
                 }
+                dispatchSessionFinished(returnCode, msg, extras);
             }
         };
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index be2bdaa..fd2256f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -230,6 +230,8 @@
                     return runDexoptJob();
                 case "cancel-bg-dexopt-job":
                     return cancelBgDexOptJob();
+                case "delete-dexopt":
+                    return runDeleteDexOpt();
                 case "dump-profiles":
                     return runDumpProfiles();
                 case "snapshot-profile":
@@ -1917,6 +1919,24 @@
         return 0;
     }
 
+    private int runDeleteDexOpt() throws RemoteException {
+        PrintWriter pw = getOutPrintWriter();
+        String packageName = getNextArg();
+        if (TextUtils.isEmpty(packageName)) {
+            pw.println("Error: no package name");
+            return 1;
+        }
+        long freedBytes = LocalServices.getService(
+                PackageManagerInternal.class).deleteOatArtifactsOfPackage(packageName);
+        if (freedBytes < 0) {
+            pw.println("Error: delete failed");
+            return 1;
+        }
+        pw.println("Success: freed " + freedBytes + " bytes");
+        Slog.i(TAG, "delete-dexopt " + packageName + " ,freed " + freedBytes + " bytes");
+        return 0;
+    }
+
     private int runDumpProfiles() throws RemoteException {
         String packageName = getNextArg();
         mInterface.dumpProfiles(packageName);
@@ -2745,7 +2765,7 @@
         IUserManager um = IUserManager.Stub.asInterface(
                 ServiceManager.getService(Context.USER_SERVICE));
         if (setEphemeralIfInUse) {
-            return removeUserOrSetEphemeral(um, userId);
+            return removeUserWhenPossible(um, userId);
         } else {
             final boolean success = wait ? removeUserAndWait(um, userId) : removeUser(um, userId);
             if (success) {
@@ -2808,10 +2828,10 @@
         }
     }
 
-    private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId)
+    private int removeUserWhenPossible(IUserManager um, @UserIdInt int userId)
             throws RemoteException {
         Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use.");
-        int result = um.removeUserOrSetEphemeral(userId, /* evenWhenDisallowed= */ false);
+        int result = um.removeUserWhenPossible(userId, /* overrideDevicePolicy= */ false);
         switch (result) {
             case UserManager.REMOVE_RESULT_REMOVED:
                 getOutPrintWriter().printf("Success: user %d removed\n", userId);
@@ -4000,6 +4020,9 @@
         pw.println("  force-dex-opt PACKAGE");
         pw.println("    Force immediate execution of dex opt for the given PACKAGE.");
         pw.println("");
+        pw.println("  delete-dexopt PACKAGE");
+        pw.println("    Delete dex optimization results for the given PACKAGE.");
+        pw.println("");
         pw.println("  bg-dexopt-job");
         pw.println("    Execute the background optimizations immediately.");
         pw.println("    Note that the command only runs the background optimizer logic. It may");
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 4bcc2a3..f87063a 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -56,27 +56,6 @@
       ]
     },
     {
-      "name": "CtsAppSecurityHostTestCases",
-      "options": [
-        {
-          "include-filter": "android.appsecurity.cts.PrivilegedUpdateTests"
-        }
-      ]
-    },
-    {
-      "name": "CtsAppSecurityHostTestCases",
-      "file_patterns": [
-        "core/java/.*Install.*",
-        "services/core/.*Install.*",
-        "services/core/java/com/android/server/pm/.*"
-      ],
-      "options": [
-        {
-          "include-filter": "android.appsecurity.cts.SplitTests"
-        }
-      ]
-    },
-    {
       "name": "PackageManagerServiceHostTests",
       "options": [
         {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index fb3ca91..d29dbbc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2487,20 +2487,25 @@
                 return false;
             }
 
-            // Limit the number of profiles that can be created
-            final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
-            if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
-                final int userTypeCount = getProfileIds(userId, userType, false).length;
-                final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
-                if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+            final int userTypeCount = getProfileIds(userId, userType, false).length;
+            final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
+            final int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+                    - profilesRemovedCount;
+
+            // Limit total number of users that can be created
+            if (usersCountAfterRemoving >= UserManager.getMaxSupportedUsers()) {
+                // Special case: Allow creating a managed profile anyway if there's only 1 user
+                // Otherwise, disallow.
+                if (!(isManagedProfile && usersCountAfterRemoving == 1)) {
                     return false;
                 }
-                // Allow creating a managed profile in the special case where there is only one user
-                if (isManagedProfile) {
-                    int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
-                            - profilesRemovedCount;
-                    return usersCountAfterRemoving == 1
-                            || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+            }
+
+            // Limit the number of profiles of this type that can be created.
+            final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
+            if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+                if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+                    return false;
                 }
             }
         }
@@ -3753,6 +3758,7 @@
         final boolean isGuest = UserManager.isUserTypeGuest(userType);
         final boolean isRestricted = UserManager.isUserTypeRestricted(userType);
         final boolean isDemo = UserManager.isUserTypeDemo(userType);
+        final boolean isManagedProfile = UserManager.isUserTypeManagedProfile(userType);
 
         final long ident = Binder.clearCallingIdentity();
         UserInfo userInfo;
@@ -3776,6 +3782,14 @@
                                     + ". Maximum number of that type already exists.",
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
+                if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
+                    // If the user limit has been reached, we cannot add a user (except guest/demo).
+                    // Note that managed profiles can bypass it in certain circumstances (taken
+                    // into account in the profile check below).
+                    throwCheckedUserOperationException(
+                            "Cannot add user. Maximum user limit is reached.",
+                            UserManager.USER_OPERATION_ERROR_MAX_USERS);
+                }
                 // TODO(b/142482943): Perhaps let the following code apply to restricted users too.
                 if (isProfile && !canAddMoreProfilesToUser(userType, parentId, false)) {
                     throwCheckedUserOperationException(
@@ -3783,13 +3797,6 @@
                                     + " for user " + parentId,
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
-                if (!isGuest && !isProfile && !isDemo && isUserLimitReached()) {
-                    // If we're not adding a guest/demo user or a profile and the 'user limit' has
-                    // been reached, cannot add a user.
-                    throwCheckedUserOperationException(
-                            "Cannot add user. Maximum user limit is reached.",
-                            UserManager.USER_OPERATION_ERROR_MAX_USERS);
-                }
                 // In legacy mode, restricted profile's parent can only be the owner user
                 if (isRestricted && !UserManager.isSplitSystemUser()
                         && (parentId != UserHandle.USER_SYSTEM)) {
@@ -4430,11 +4437,11 @@
     }
 
     @Override
-    public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId,
-            boolean evenWhenDisallowed) {
+    public @UserManager.RemoveResult int removeUserWhenPossible(@UserIdInt int userId,
+            boolean overrideDevicePolicy) {
         checkCreateUsersPermission("Only the system can remove users");
 
-        if (!evenWhenDisallowed) {
+        if (!overrideDevicePolicy) {
             final String restriction = getUserRemovalRestriction(userId);
             if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
                 Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 27a16e9..17a5fd0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -94,6 +94,7 @@
     private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
     private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
     private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
+    private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
 
     /** Interface that allows reading the device state configuration. */
     interface ReadableConfig {
@@ -141,8 +142,8 @@
                         for (int i = 0; i < configFlagStrings.size(); i++) {
                             final String configFlagString = configFlagStrings.get(i);
                             switch (configFlagString) {
-                                case "FLAG_CANCEL_STICKY_REQUESTS":
-                                    flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS;
+                                case FLAG_CANCEL_OVERRIDE_REQUESTS:
+                                    flags |= DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
                                     break;
                                 default:
                                     Slog.w(TAG, "Parsed unknown flag with name: "
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4b2770c..abfa016 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -102,7 +102,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.os.BackgroundThread;
@@ -290,7 +289,6 @@
     private BatteryManagerInternal mBatteryManagerInternal;
     private DisplayManagerInternal mDisplayManagerInternal;
     private IBatteryStats mBatteryStats;
-    private IAppOpsService mAppOps;
     private WindowManagerPolicy mPolicy;
     private Notifier mNotifier;
     private WirelessChargerDetector mWirelessChargerDetector;
@@ -298,7 +296,7 @@
     private DreamManagerInternal mDreamManager;
     private LogicalLight mAttentionLight;
 
-    private InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
+    private final InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
     private final AmbientDisplaySuppressionController mAmbientDisplaySuppressionController;
 
     private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER);
@@ -318,10 +316,10 @@
 
     // Table of all suspend blockers.
     // There should only be a few of these.
-    private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>();
+    private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<>();
 
     // Table of all wake locks acquired by applications.
-    private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>();
+    private final ArrayList<WakeLock> mWakeLocks = new ArrayList<>();
 
     // A bitfield that summarizes the state of all active wakelocks.
     private int mWakeLockSummary;
@@ -354,8 +352,6 @@
     private long mLastScreenBrightnessBoostTime;
     private boolean mScreenBrightnessBoostInProgress;
 
-    private DisplayGroupPowerChangeListener mDisplayGroupPowerChangeListener;
-
     // The suspend blocker used to keep the CPU alive while the device is booting.
     private final SuspendBlocker mBootingSuspendBlocker;
 
@@ -938,7 +934,7 @@
          * Handler for asynchronous operations performed by the power manager.
          */
         Handler createHandler(Looper looper, Handler.Callback callback) {
-            return new Handler(looper, callback, true /*async*/);
+            return new Handler(looper, callback, /* async= */ true);
         }
 
         void invalidateIsInteractiveCaches() {
@@ -973,7 +969,7 @@
         mInjector = injector;
 
         mHandlerThread = new ServiceThread(TAG,
-                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+                Process.THREAD_PRIORITY_DISPLAY, /* allowIo= */ false);
         mHandlerThread.start();
         mHandler = injector.createHandler(mHandlerThread.getLooper(),
                 new PowerManagerHandlerCallback());
@@ -1160,18 +1156,18 @@
         }
     }
 
-    public void systemReady(IAppOpsService appOps) {
+    public void systemReady() {
         synchronized (mLock) {
             mSystemReady = true;
-            mAppOps = appOps;
             mDreamManager = getLocalService(DreamManagerInternal.class);
             mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
             mPolicy = getLocalService(WindowManagerPolicy.class);
             mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
             mAttentionDetector.systemReady(mContext);
             mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP, new PowerGroup());
-            mDisplayGroupPowerChangeListener = new DisplayGroupPowerChangeListener();
-            mDisplayManagerInternal.registerDisplayGroupListener(mDisplayGroupPowerChangeListener);
+            DisplayGroupPowerChangeListener displayGroupPowerChangeListener =
+                    new DisplayGroupPowerChangeListener();
+            mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener);
 
             SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
 
@@ -1723,6 +1719,7 @@
     }
 
     // Called from native code.
+    @SuppressWarnings("unused")
     private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
         userActivityInternal(displayId, eventTime, event, flags, Process.SYSTEM_UID);
     }
@@ -1757,7 +1754,7 @@
             if (userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
                     mClock.uptimeMillis(),
                     PowerManager.USER_ACTIVITY_EVENT_ATTENTION,
-                    0 /* flags */,
+                    /* flags= */ 0,
                     Process.SYSTEM_UID)) {
                 updatePowerStateLocked();
             }
@@ -2046,6 +2043,7 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     @GuardedBy("mLock")
     private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,
             int opUid, String opPackageName, String details) {
@@ -2491,9 +2489,8 @@
      * Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
      * Note that most wake-locks are ignored when the system is asleep.
      *
-     * This function must have no other side-effects.
+     * This function must have no other side effects.
      */
-    @SuppressWarnings("deprecation")
     @GuardedBy("mLock")
     private void updateWakeLockSummaryLocked(int dirty) {
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS | DIRTY_DISPLAY_GROUP_WAKEFULNESS))
@@ -2596,6 +2593,7 @@
     }
 
     /** Get wake lock summary flags that correspond to the given wake lock. */
+    @SuppressWarnings("deprecation")
     private int getWakeLockSummaryFlags(WakeLock wakeLock) {
         switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.PARTIAL_WAKE_LOCK:
@@ -3170,7 +3168,7 @@
         if (mDreamManager != null) {
             // Restart the dream whenever the sandman is summoned.
             if (startDreaming) {
-                mDreamManager.stopDream(false /*immediate*/);
+                mDreamManager.stopDream(/* immediate= */ false);
                 mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
             }
             isDreaming = mDreamManager.isDreaming();
@@ -3254,7 +3252,7 @@
 
         // Stop dream.
         if (isDreaming) {
-            mDreamManager.stopDream(false /*immediate*/);
+            mDreamManager.stopDream(/* immediate= */ false);
         }
     }
 
@@ -3518,7 +3516,7 @@
                 mDirty |= DIRTY_PROXIMITY_POSITIVE;
                 userActivityNoUpdateLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP),
                         mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                        0 /* flags */, Process.SYSTEM_UID);
+                        /* flags= */ 0, Process.SYSTEM_UID);
                 updatePowerStateLocked();
             }
         }
@@ -4302,7 +4300,7 @@
         if (sQuiescent) {
             // Pass the optional "quiescent" argument to the bootloader to let it know
             // that it should not turn the screen/lights on.
-            if (reason != ""){
+            if (!"".equals(reason)) {
                 reason += ",";
             }
             reason = reason + "quiescent";
@@ -5412,8 +5410,8 @@
                 ws = new WorkSource();
                 // XXX should WorkSource have a way to set uids as an int[] instead of adding them
                 // one at a time?
-                for (int i = 0; i < uids.length; i++) {
-                    ws.add(uids[i]);
+                for (int uid : uids) {
+                    ws.add(uid);
                 }
             }
             updateWakeLockWorkSource(lock, ws, null);
@@ -5954,7 +5952,8 @@
             // if uid is of root's, we permit this operation straight away
             if (uid != Process.ROOT_UID) {
                 if (!Settings.checkAndNoteWriteSettingsOperation(mContext, uid,
-                        Settings.getPackageNameForUid(mContext, uid), true)) {
+                        Settings.getPackageNameForUid(mContext, uid), /* attributionTag= */ null,
+                        /* throwException= */ true)) {
                     return;
                 }
             }
@@ -6118,6 +6117,7 @@
             for (String arg : args) {
                 if (arg.equals("--proto")) {
                     isDumpProto = true;
+                    break;
                 }
             }
             try {
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7f50cd6..16176f0 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3435,7 +3435,11 @@
                     convertTimeZoneSuggestionToProtoBytes(
                             metricsState.getLatestTelephonySuggestion()),
                     convertTimeZoneSuggestionToProtoBytes(
-                            metricsState.getLatestGeolocationSuggestion())
+                            metricsState.getLatestGeolocationSuggestion()),
+                    metricsState.isTelephonyTimeZoneFallbackSupported(),
+                    metricsState.getDeviceTimeZoneId(),
+                    metricsState.isEnhancedMetricsCollectionEnabled(),
+                    metricsState.getGeoDetectionRunInBackgroundEnabled()
             ));
         } catch (RuntimeException e) {
             Slog.e(TAG, "Getting time zone detection state failed: ", e);
@@ -3482,6 +3486,14 @@
                         android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
                         zoneIdOrdinal);
             }
+            String[] zoneIds = suggestion.getZoneIds();
+            if (zoneIds != null) {
+                for (String zoneId : zoneIds) {
+                    protoOutputStream.write(
+                            android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_IDS,
+                            zoneId);
+                }
+            }
         }
         protoOutputStream.flush();
         closeQuietly(byteArrayOutputStream);
@@ -4587,7 +4599,8 @@
         int matchContentFrameRateUserPreference =
                 displayManager.getMatchContentFrameRateUserPreference();
         byte[] userDisabledHdrTypes = toBytes(displayManager.getUserDisabledHdrTypes());
-        Display.Mode userPreferredDisplayMode = displayManager.getUserPreferredDisplayMode();
+        Display.Mode userPreferredDisplayMode =
+                displayManager.getGlobalUserPreferredDisplayMode();
         int userPreferredWidth = userPreferredDisplayMode != null
                 ? userPreferredDisplayMode.getPhysicalWidth() : -1;
         int userPreferredHeight = userPreferredDisplayMode != null
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index b23f11a..36ab111d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -281,15 +281,7 @@
                 LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
                 LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
                 LocationTimeZoneProviderController.MetricsLogger metricsLogger =
-                        new LocationTimeZoneProviderController.MetricsLogger() {
-                            @Override
-                            public void onStateChange(
-                                    @LocationTimeZoneProviderController.State String state) {
-                                // TODO b/200279201 - wire this up to metrics code
-                                // No-op.
-                            }
-                        };
-
+                        new RealControllerMetricsLogger();
                 boolean recordStateChanges = mServiceConfigAccessor.getRecordStateChangesForTests();
                 LocationTimeZoneProviderController controller =
                         new LocationTimeZoneProviderController(mThreadingDomain, metricsLogger,
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java
new file mode 100644
index 0000000..9cb36ef
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealControllerMetricsLogger.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__CERTAIN;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__DESTROYED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__FAILED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__INITIALIZING;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__PROVIDERS_INITIALIZING;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNCERTAIN;
+import static com.android.internal.util.FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNKNOWN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_PROVIDERS_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProviderController.STATE_UNKNOWN;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.MetricsLogger} which logs
+ * using {@link FrameworkStatsLog}.
+ */
+final class RealControllerMetricsLogger
+        implements LocationTimeZoneProviderController.MetricsLogger {
+
+    RealControllerMetricsLogger() {
+    }
+
+    @Override
+    public void onStateChange(@State String state) {
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED,
+                metricsState(state));
+    }
+
+    private static int metricsState(@State String state) {
+        switch (state) {
+            case STATE_PROVIDERS_INITIALIZING:
+                // Disable lint check (line length) for generated long constant name.
+                // CHECKSTYLE:OFF Generated code
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__PROVIDERS_INITIALIZING;
+                // CHECKSTYLE:ON Generated code
+            case STATE_STOPPED:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__STOPPED;
+            case STATE_INITIALIZING:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__INITIALIZING;
+            case STATE_CERTAIN:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__CERTAIN;
+            case STATE_UNCERTAIN:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNCERTAIN;
+            case STATE_DESTROYED:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__DESTROYED;
+            case STATE_FAILED:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__FAILED;
+            case STATE_UNKNOWN:
+            default:
+                return LOCATION_TIME_ZONE_PROVIDER_CONTROLLER_STATE_CHANGED__STATE__UNKNOWN;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
index e19ec84..fe543ad 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
@@ -41,12 +41,12 @@
  * The real implementation of {@link ProviderMetricsLogger} which logs using
  * {@link FrameworkStatsLog}.
  */
-public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+final class RealProviderMetricsLogger implements ProviderMetricsLogger {
 
     @IntRange(from = 0, to = 1)
     private final int mProviderIndex;
 
-    public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+    RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
         mProviderIndex = providerIndex;
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 59f8e54..79231f7 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -123,6 +123,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_GRANT_TRUST:
+                    // TODO(b/213631675): Respect FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
                     if (!isConnected()) {
                         Log.w(TAG, "Agent is not connected, cannot grant trust: "
                                 + mName.flattenToShortString());
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4b71742..fc87253 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -46,6 +46,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
@@ -123,6 +124,8 @@
     private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
     private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
 
+    private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
+
     private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
     private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
     private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000;
@@ -635,11 +638,18 @@
         }
     }
 
+    private void refreshDeviceLockedForUser(int userId) {
+        refreshDeviceLockedForUser(userId, UserHandle.USER_NULL);
+    }
+
     /**
      * Update the user's locked state. Only applicable to users with a real keyguard
      * ({@link UserInfo#supportsSwitchToByUser}) and unsecured managed profiles.
+     *
+     * If this is called due to an unlock operation set unlockedUser to prevent the lock from
+     * being prematurely reset for that user while keyguard is still in the process of going away.
      */
-    private void refreshDeviceLockedForUser(int userId) {
+    private void refreshDeviceLockedForUser(int userId, int unlockedUser) {
         if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_SYSTEM) {
             Log.e(TAG, "refreshDeviceLockedForUser(userId=" + userId + "): Invalid user handle,"
                     + " must be USER_ALL or a specific user.", new Throwable("here"));
@@ -675,6 +685,7 @@
             boolean trusted = aggregateIsTrusted(id);
             boolean showingKeyguard = true;
             boolean biometricAuthenticated = false;
+            boolean currentUserIsUnlocked = false;
 
             if (mCurrentUser == id) {
                 synchronized(mUsersUnlockedByBiometric) {
@@ -683,10 +694,17 @@
                 try {
                     showingKeyguard = wm.isKeyguardLocked();
                 } catch (RemoteException e) {
+                    Log.w(TAG, "Unable to check keyguard lock state", e);
                 }
+                currentUserIsUnlocked = unlockedUser == id;
             }
-            boolean deviceLocked = secure && showingKeyguard && !trusted &&
-                    !biometricAuthenticated;
+            final boolean deviceLocked = secure && showingKeyguard && !trusted
+                    && !biometricAuthenticated;
+            if (deviceLocked && currentUserIsUnlocked) {
+                // keyguard is finishing but may not have completed going away yet
+                continue;
+            }
+
             setDeviceLockedForUser(id, deviceLocked);
         }
     }
@@ -1306,13 +1324,20 @@
         }
 
         @Override
-        public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
+        public void clearAllBiometricRecognized(
+                BiometricSourceType biometricSource, int unlockedUser) {
             enforceReportPermission();
             synchronized(mUsersUnlockedByBiometric) {
                 mUsersUnlockedByBiometric.clear();
             }
-            mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
-                    0 /* arg2 */).sendToTarget();
+            Message message = mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER,
+                    UserHandle.USER_ALL, 0 /* arg2 */);
+            if (unlockedUser >= 0) {
+                Bundle bundle = new Bundle();
+                bundle.putInt(REFRESH_DEVICE_LOCKED_EXCEPT_USER, unlockedUser);
+                message.setData(bundle);
+            }
+            message.sendToTarget();
         }
     };
 
@@ -1404,9 +1429,11 @@
                     break;
                 case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
                     if (msg.arg2 == 1) {
-                        updateTrust(msg.arg1, 0 /* flags */, true);
+                        updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */);
                     }
-                    refreshDeviceLockedForUser(msg.arg1);
+                    final int unlockedUser = msg.getData().getInt(
+                            REFRESH_DEVICE_LOCKED_EXCEPT_USER, UserHandle.USER_NULL);
+                    refreshDeviceLockedForUser(msg.arg1, unlockedUser);
                     break;
                 case MSG_SCHEDULE_TRUST_TIMEOUT:
                     handleScheduleTrustTimeout(msg.arg1, msg.arg2);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e066ca3..e786fa2 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1851,18 +1851,19 @@
         }
 
         @Override
-        public void setIAppNotificationEnabled(IBinder sessionToken, boolean enabled, int userId) {
+        public void setInteractiveAppNotificationEnabled(
+                IBinder sessionToken, boolean enabled, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "setIAppNotificationEnabled");
+                    userId, "setInteractiveAppNotificationEnabled");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .setIAppNotificationEnabled(enabled);
+                                .setInteractiveAppNotificationEnabled(enabled);
                     } catch (RemoteException | SessionNotFoundException e) {
-                        Slog.e(TAG, "error in setIAppNotificationEnabled", e);
+                        Slog.e(TAG, "error in setInteractiveAppNotificationEnabled", e);
                     }
                 }
             } finally {
@@ -3538,6 +3539,23 @@
         }
 
         @Override
+        public void onSignalStrength(int strength) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onSignalStrength(" + strength + ")");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onSignalStrength(strength, mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onSignalStrength", e);
+                }
+            }
+        }
+
+        @Override
         public void onTuned(Uri channelUri) {
             synchronized (mLock) {
                 if (DEBUG) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index a4732c1..6058d88 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -34,15 +34,15 @@
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppClient;
 import android.media.tv.interactive.ITvIAppManager;
-import android.media.tv.interactive.ITvIAppManagerCallback;
-import android.media.tv.interactive.ITvIAppService;
-import android.media.tv.interactive.ITvIAppServiceCallback;
-import android.media.tv.interactive.ITvIAppSession;
-import android.media.tv.interactive.ITvIAppSessionCallback;
-import android.media.tv.interactive.TvIAppInfo;
+import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
+import android.media.tv.interactive.ITvInteractiveAppService;
+import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
+import android.media.tv.interactive.ITvInteractiveAppSession;
+import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
 import android.media.tv.interactive.TvIAppService;
+import android.media.tv.interactive.TvInteractiveAppInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -53,6 +53,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InputChannel;
@@ -112,54 +113,55 @@
     }
 
     @GuardedBy("mLock")
-    private void buildTvIAppServiceListLocked(int userId, String[] updatedPackages) {
+    private void buildTvInteractiveAppServiceListLocked(int userId, String[] updatedPackages) {
         UserState userState = getOrCreateUserStateLocked(userId);
         userState.mPackageSet.clear();
 
         if (DEBUG) {
-            Slogf.d(TAG, "buildTvIAppServiceListLocked");
+            Slogf.d(TAG, "buildTvInteractiveAppServiceListLocked");
         }
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> services = pm.queryIntentServicesAsUser(
                 new Intent(TvIAppService.SERVICE_INTERFACE),
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                 userId);
-        List<TvIAppInfo> iAppList = new ArrayList<>();
+        List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
 
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
-            // TODO: add BIND_TV_IAPP permission and check it here
+            // TODO: add BIND_TV_INTERACTIVE_APP permission and check it here
 
             ComponentName component = new ComponentName(si.packageName, si.name);
             try {
-                TvIAppInfo info = new TvIAppInfo.Builder(mContext, component).build();
+                TvInteractiveAppInfo info =
+                        new TvInteractiveAppInfo(mContext, component);
                 iAppList.add(info);
             } catch (Exception e) {
-                Slogf.e(TAG, "failed to load TV IApp service " + si.name, e);
+                Slogf.e(TAG, "failed to load TV Interactive App service " + si.name, e);
                 continue;
             }
             userState.mPackageSet.add(si.packageName);
         }
 
         // sort the iApp list by iApp service id
-        Collections.sort(iAppList, Comparator.comparing(TvIAppInfo::getId));
-        Map<String, TvIAppState> iAppMap = new HashMap<>();
+        Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppInfo::getId));
+        Map<String, TvInteractiveAppState> iAppMap = new HashMap<>();
         ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size());
-        for (TvIAppInfo info : iAppList) {
+        for (TvInteractiveAppInfo info : iAppList) {
             String iAppServiceId = info.getId();
             if (DEBUG) {
                 Slogf.d(TAG, "add " + iAppServiceId);
             }
-            // Running count of IApp for each IApp service
+            // Running count of Interactive App for each Interactive App service
             Integer count = tiasAppCount.get(iAppServiceId);
             count = count == null ? 1 : count + 1;
             tiasAppCount.put(iAppServiceId, count);
-            TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+            TvInteractiveAppState iAppState = userState.mIAppMap.get(iAppServiceId);
             if (iAppState == null) {
-                iAppState = new TvIAppState();
+                iAppState = new TvInteractiveAppState();
             }
             iAppState.mInfo = info;
-            iAppState.mUid = getIAppUid(info);
+            iAppState.mUid = getInteractiveAppUid(info);
             iAppState.mComponentName = info.getComponent();
             iAppMap.put(iAppServiceId, iAppState);
             iAppState.mIAppNumber = count;
@@ -167,14 +169,14 @@
 
         for (String iAppServiceId : iAppMap.keySet()) {
             if (!userState.mIAppMap.containsKey(iAppServiceId)) {
-                notifyIAppServiceAddedLocked(userState, iAppServiceId);
+                notifyInteractiveAppServiceAddedLocked(userState, iAppServiceId);
             } else if (updatedPackages != null) {
                 // Notify the package updates
                 ComponentName component = iAppMap.get(iAppServiceId).mInfo.getComponent();
                 for (String updatedPackage : updatedPackages) {
                     if (component.getPackageName().equals(updatedPackage)) {
                         updateServiceConnectionLocked(component, userId);
-                        notifyIAppServiceUpdatedLocked(userState, iAppServiceId);
+                        notifyInteractiveAppServiceUpdatedLocked(userState, iAppServiceId);
                         break;
                     }
                 }
@@ -183,12 +185,12 @@
 
         for (String iAppServiceId : userState.mIAppMap.keySet()) {
             if (!iAppMap.containsKey(iAppServiceId)) {
-                TvIAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
+                TvInteractiveAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
                 ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent());
                 if (serviceState != null) {
                     abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId);
                 }
-                notifyIAppServiceRemovedLocked(userState, iAppServiceId);
+                notifyInteractiveAppServiceRemovedLocked(userState, iAppServiceId);
             }
         }
 
@@ -197,48 +199,56 @@
     }
 
     @GuardedBy("mLock")
-    private void notifyIAppServiceAddedLocked(UserState userState, String iAppServiceId) {
+    private void notifyInteractiveAppServiceAddedLocked(UserState userState, String iAppServiceId) {
         if (DEBUG) {
-            Slog.d(TAG, "notifyIAppServiceAddedLocked(iAppServiceId=" + iAppServiceId + ")");
+            Slog.d(TAG, "notifyInteractiveAppServiceAddedLocked(iAppServiceId="
+                    + iAppServiceId + ")");
         }
         int n = userState.mCallbacks.beginBroadcast();
         for (int i = 0; i < n; ++i) {
             try {
-                userState.mCallbacks.getBroadcastItem(i).onIAppServiceAdded(iAppServiceId);
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onInteractiveAppServiceAdded(iAppServiceId);
             } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report added IApp service to callback", e);
+                Slog.e(TAG, "failed to report added Interactive App service to callback", e);
             }
         }
         userState.mCallbacks.finishBroadcast();
     }
 
     @GuardedBy("mLock")
-    private void notifyIAppServiceRemovedLocked(UserState userState, String iAppServiceId) {
+    private void notifyInteractiveAppServiceRemovedLocked(
+            UserState userState, String iAppServiceId) {
         if (DEBUG) {
-            Slog.d(TAG, "notifyIAppServiceRemovedLocked(iAppServiceId=" + iAppServiceId + ")");
+            Slog.d(TAG, "notifyInteractiveAppServiceRemovedLocked(iAppServiceId="
+                    + iAppServiceId + ")");
         }
         int n = userState.mCallbacks.beginBroadcast();
         for (int i = 0; i < n; ++i) {
             try {
-                userState.mCallbacks.getBroadcastItem(i).onIAppServiceRemoved(iAppServiceId);
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onInteractiveAppServiceRemoved(iAppServiceId);
             } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report removed IApp service to callback", e);
+                Slog.e(TAG, "failed to report removed Interactive App service to callback", e);
             }
         }
         userState.mCallbacks.finishBroadcast();
     }
 
     @GuardedBy("mLock")
-    private void notifyIAppServiceUpdatedLocked(UserState userState, String iAppServiceId) {
+    private void notifyInteractiveAppServiceUpdatedLocked(
+            UserState userState, String iAppServiceId) {
         if (DEBUG) {
-            Slog.d(TAG, "notifyIAppServiceUpdatedLocked(iAppServiceId=" + iAppServiceId + ")");
+            Slog.d(TAG, "notifyInteractiveAppServiceUpdatedLocked(iAppServiceId="
+                    + iAppServiceId + ")");
         }
         int n = userState.mCallbacks.beginBroadcast();
         for (int i = 0; i < n; ++i) {
             try {
-                userState.mCallbacks.getBroadcastItem(i).onIAppServiceUpdated(iAppServiceId);
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onInteractiveAppServiceUpdated(iAppServiceId);
             } catch (RemoteException e) {
-                Slog.e(TAG, "failed to report updated IApp service to callback", e);
+                Slog.e(TAG, "failed to report updated Interactive App service to callback", e);
             }
         }
         userState.mCallbacks.finishBroadcast();
@@ -262,7 +272,7 @@
         userState.mCallbacks.finishBroadcast();
     }
 
-    private int getIAppUid(TvIAppInfo info) {
+    private int getInteractiveAppUid(TvInteractiveAppInfo info) {
         try {
             return getContext().getPackageManager().getApplicationInfo(
                     info.getServiceInfo().packageName, 0).uid;
@@ -286,18 +296,18 @@
             registerBroadcastReceivers();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mLock) {
-                buildTvIAppServiceListLocked(mCurrentUserId, null);
+                buildTvInteractiveAppServiceListLocked(mCurrentUserId, null);
             }
         }
     }
 
     private void registerBroadcastReceivers() {
         PackageMonitor monitor = new PackageMonitor() {
-            private void buildTvIAppServiceList(String[] packages) {
+            private void buildTvInteractiveAppServiceList(String[] packages) {
                 int userId = getChangingUserId();
                 synchronized (mLock) {
                     if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
-                        buildTvIAppServiceListLocked(userId, packages);
+                        buildTvInteractiveAppServiceListLocked(userId, packages);
                     }
                 }
             }
@@ -305,9 +315,9 @@
             @Override
             public void onPackageUpdateFinished(String packageName, int uid) {
                 if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
-                // This callback is invoked when the TV iApp service is reinstalled.
+                // This callback is invoked when the TV interactive App service is reinstalled.
                 // In this case, isReplacing() always returns true.
-                buildTvIAppServiceList(new String[] { packageName });
+                buildTvInteractiveAppServiceList(new String[] { packageName });
             }
 
             @Override
@@ -318,7 +328,7 @@
                 // This callback is invoked when the media on which some packages exist become
                 // available.
                 if (isReplacing()) {
-                    buildTvIAppServiceList(packages);
+                    buildTvInteractiveAppServiceList(packages);
                 }
             }
 
@@ -331,7 +341,7 @@
                             + ")");
                 }
                 if (isReplacing()) {
-                    buildTvIAppServiceList(packages);
+                    buildTvInteractiveAppServiceList(packages);
                 }
             }
 
@@ -339,17 +349,19 @@
             public void onSomePackagesChanged() {
                 if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
                 if (isReplacing()) {
-                    if (DEBUG) Slogf.d(TAG, "Skipped building TV iApp list due to replacing");
-                    // When the package is updated, buildTvIAppServiceListLocked is called in other
-                    // methods instead.
+                    if (DEBUG) {
+                        Slogf.d(TAG, "Skipped building TV interactive App list due to replacing");
+                    }
+                    // When the package is updated, buildTvInteractiveAppServiceListLocked is called
+                    // in other methods instead.
                     return;
                 }
-                buildTvIAppServiceList(null);
+                buildTvInteractiveAppServiceList(null);
             }
 
             @Override
             public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                // The iApp list needs to be updated in any cases, regardless of whether
+                // The interactive App list needs to be updated in any cases, regardless of whether
                 // it happened to the whole package or a specific component. Returning true so that
                 // the update can be handled in {@link #onSomePackagesChanged}.
                 return true;
@@ -401,7 +413,7 @@
             unbindServiceOfUserLocked(mCurrentUserId);
 
             mCurrentUserId = userId;
-            buildTvIAppServiceListLocked(userId, null);
+            buildTvInteractiveAppServiceListLocked(userId, null);
         }
     }
 
@@ -486,7 +498,7 @@
     @GuardedBy("mLock")
     private void startProfileLocked(int userId) {
         mRunningProfiles.add(userId);
-        buildTvIAppServiceListLocked(userId, null);
+        buildTvInteractiveAppServiceListLocked(userId, null);
     }
 
     @GuardedBy("mLock")
@@ -601,13 +613,14 @@
     }
 
     @GuardedBy("mLock")
-    private ITvIAppSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+    private ITvInteractiveAppSession getSessionLocked(
+            IBinder sessionToken, int callingUid, int userId) {
         return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
     }
 
     @GuardedBy("mLock")
-    private ITvIAppSession getSessionLocked(SessionState sessionState) {
-        ITvIAppSession session = sessionState.mSession;
+    private ITvInteractiveAppSession getSessionLocked(SessionState sessionState) {
+        ITvInteractiveAppSession session = sessionState.mSession;
         if (session == null) {
             throw new IllegalStateException("Session not yet created for token "
                     + sessionState.mSessionToken);
@@ -618,15 +631,15 @@
     private final class BinderService extends ITvIAppManager.Stub {
 
         @Override
-        public List<TvIAppInfo> getTvIAppServiceList(int userId) {
+        public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "getTvIAppServiceList");
+                    Binder.getCallingUid(), userId, "getTvInteractiveAppServiceList");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    List<TvIAppInfo> iAppList = new ArrayList<>();
-                    for (TvIAppState state : userState.mIAppMap.values()) {
+                    List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
+                    for (TvInteractiveAppState state : userState.mIAppMap.values()) {
                         iAppList.add(state.mInfo);
                     }
                     return iAppList;
@@ -645,7 +658,7 @@
             try {
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
                     if (iAppState == null) {
                         Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId);
                         return;
@@ -673,16 +686,16 @@
         }
 
         @Override
-        public void notifyAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+        public void registerAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "notifyAppLinkInfo");
+                    Binder.getCallingUid(), userId, "registerAppLinkInfo");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
                     if (iAppState == null) {
-                        Slogf.e(TAG, "failed to notifyAppLinkInfo - unknown TIAS id "
+                        Slogf.e(TAG, "failed to registerAppLinkInfo - unknown TIAS id "
                                 + tiasId);
                         return;
                     }
@@ -691,18 +704,54 @@
                     if (serviceState == null) {
                         serviceState = new ServiceState(
                                 componentName, tiasId, resolvedUserId);
-                        serviceState.addPendingAppLink(appLinkInfo);
+                        serviceState.addPendingAppLink(appLinkInfo, true);
                         userState.mServiceStateMap.put(componentName, serviceState);
                         updateServiceConnectionLocked(componentName, resolvedUserId);
                     } else if (serviceState.mService != null) {
-                        serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+                        serviceState.mService.registerAppLinkInfo(appLinkInfo);
                     } else {
-                        serviceState.addPendingAppLink(appLinkInfo);
+                        serviceState.addPendingAppLink(appLinkInfo, true);
                         updateServiceConnectionLocked(componentName, resolvedUserId);
                     }
                 }
             } catch (RemoteException e) {
-                Slogf.e(TAG, "error in notifyAppLinkInfo", e);
+                Slogf.e(TAG, "error in registerAppLinkInfo", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void unregisterAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "unregisterAppLinkInfo");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
+                    if (iAppState == null) {
+                        Slogf.e(TAG, "failed to unregisterAppLinkInfo - unknown TIAS id "
+                                + tiasId);
+                        return;
+                    }
+                    ComponentName componentName = iAppState.mInfo.getComponent();
+                    ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+                    if (serviceState == null) {
+                        serviceState = new ServiceState(
+                                componentName, tiasId, resolvedUserId);
+                        serviceState.addPendingAppLink(appLinkInfo, false);
+                        userState.mServiceStateMap.put(componentName, serviceState);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    } else if (serviceState.mService != null) {
+                        serviceState.mService.unregisterAppLinkInfo(appLinkInfo);
+                    } else {
+                        serviceState.addPendingAppLink(appLinkInfo, false);
+                        updateServiceConnectionLocked(componentName, resolvedUserId);
+                    }
+                }
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "error in unregisterAppLinkInfo", e);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -716,7 +765,7 @@
             try {
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId);
                     if (iAppState == null) {
                         Slogf.e(TAG, "failed to sendAppLinkCommand - unknown TIAS id "
                                 + tiasId);
@@ -743,7 +792,8 @@
         }
 
         @Override
-        public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
+        public void createSession(
+                final ITvInteractiveAppClient client, final String iAppServiceId, int type,
                 int seq, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
@@ -760,7 +810,7 @@
                         return;
                     }
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+                    TvInteractiveAppState iAppState = userState.mIAppMap.get(iAppServiceId);
                     if (iAppState == null) {
                         Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
                         sendSessionTokenToClientLocked(client, iAppServiceId, null, null, seq);
@@ -994,13 +1044,35 @@
         }
 
         @Override
-        public void startIApp(IBinder sessionToken, int userId) {
+        public void notifySignalStrength(IBinder sessionToken, int strength, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifySignalStrength");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifySignalStrength(strength);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifySignalStrength", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void startInteractiveApp(IBinder sessionToken, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
             }
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "startIApp");
+                    userId, "startInteractiveApp");
             SessionState sessionState = null;
             final long identity = Binder.clearCallingIdentity();
             try {
@@ -1008,7 +1080,7 @@
                     try {
                         sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        getSessionLocked(sessionState).startIApp();
+                        getSessionLocked(sessionState).startInteractiveApp();
                     } catch (RemoteException | SessionNotFoundException e) {
                         Slogf.e(TAG, "error in start", e);
                     }
@@ -1019,13 +1091,13 @@
         }
 
         @Override
-        public void stopIApp(IBinder sessionToken, int userId) {
+        public void stopInteractiveApp(IBinder sessionToken, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "BinderService#stop(userId=%d)", userId);
             }
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "stopIApp");
+                    userId, "stopInteractiveApp");
             SessionState sessionState = null;
             final long identity = Binder.clearCallingIdentity();
             try {
@@ -1033,7 +1105,7 @@
                     try {
                         sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        getSessionLocked(sessionState).stopIApp();
+                        getSessionLocked(sessionState).stopInteractiveApp();
                     } catch (RemoteException | SessionNotFoundException e) {
                         Slogf.e(TAG, "error in stop", e);
                     }
@@ -1044,6 +1116,31 @@
         }
 
         @Override
+        public void resetInteractiveApp(IBinder sessionToken, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "BinderService#reset(userId=%d)", userId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "resetInteractiveApp");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).resetInteractiveApp();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in reset", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void createBiInteractiveApp(
                 IBinder sessionToken, Uri biIAppUri, Bundle params, int userId) {
             if (DEBUG) {
@@ -1096,6 +1193,31 @@
         }
 
         @Override
+        public void setTeletextAppEnabled(IBinder sessionToken, boolean enable, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "setTeletextAppEnabled(enable=%d)", enable);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "setTeletextAppEnabled");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).setTeletextAppEnabled(enable);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in setTeletextAppEnabled", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void sendCurrentChannelUri(IBinder sessionToken, Uri channelUri, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "sendCurrentChannelUri(channelUri=%s)", channelUri.toString());
@@ -1196,6 +1318,31 @@
         }
 
         @Override
+        public void sendCurrentTvInputId(IBinder sessionToken, String inputId, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "sendCurrentTvInputId(inputId=%s)", inputId);
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "sendCurrentTvInputId");
+            SessionState sessionState = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).sendCurrentTvInputId(inputId);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in sendCurrentTvInputId", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void setSurface(IBinder sessionToken, Surface surface, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -1290,7 +1437,7 @@
         }
 
         @Override
-        public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
+        public void registerCallback(final ITvInteractiveAppManagerCallback callback, int userId) {
             int callingPid = Binder.getCallingPid();
             int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
@@ -1309,7 +1456,7 @@
         }
 
         @Override
-        public void unregisterCallback(ITvIAppManagerCallback callback, int userId) {
+        public void unregisterCallback(ITvInteractiveAppManagerCallback callback, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "unregisterCallback");
             final long identity = Binder.clearCallingIdentity();
@@ -1335,7 +1482,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .createMediaView(windowToken, frame);
-                    } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in createMediaView", e);
                     }
                 }
@@ -1355,7 +1502,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .relayoutMediaView(frame);
-                    } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in relayoutMediaView", e);
                     }
                 }
@@ -1375,7 +1522,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
                                 .removeMediaView();
-                    } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+                    } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in removeMediaView", e);
                     }
                 }
@@ -1386,8 +1533,9 @@
     }
 
     @GuardedBy("mLock")
-    private void sendSessionTokenToClientLocked(ITvIAppClient client, String iAppServiceId,
-            IBinder sessionToken, InputChannel channel, int seq) {
+    private void sendSessionTokenToClientLocked(
+            ITvInteractiveAppClient client, String iAppServiceId, IBinder sessionToken,
+            InputChannel channel, int seq) {
         try {
             client.onSessionCreated(iAppServiceId, sessionToken, channel, seq);
         } catch (RemoteException e) {
@@ -1396,8 +1544,8 @@
     }
 
     @GuardedBy("mLock")
-    private boolean createSessionInternalLocked(ITvIAppService service, IBinder sessionToken,
-            int userId) {
+    private boolean createSessionInternalLocked(
+            ITvInteractiveAppService service, IBinder sessionToken, int userId) {
         UserState userState = getOrCreateUserStateLocked(userId);
         SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
         if (DEBUG) {
@@ -1407,7 +1555,7 @@
         InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
 
         // Set up a callback to send the session token.
-        ITvIAppSessionCallback callback = new SessionCallback(sessionState, channels);
+        ITvInteractiveAppSessionCallback callback = new SessionCallback(sessionState, channels);
 
         boolean created = true;
         // Create a session. When failed, send a null token immediately.
@@ -1514,7 +1662,8 @@
         }
 
         boolean shouldBind = (!serviceState.mSessionTokens.isEmpty())
-                || (serviceState.mPendingPrepare) || (!serviceState.mPendingAppLinkInfo.isEmpty());
+                || (serviceState.mPendingPrepare)
+                || (!serviceState.mPendingAppLinkInfo.isEmpty());
 
         if (serviceState.mService == null && shouldBind) {
             // This means that the service is not yet connected but its state indicates that we
@@ -1528,7 +1677,8 @@
                 Slogf.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
             }
 
-            Intent i = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+            Intent i =
+                    new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
             serviceState.mBound = mContext.bindServiceAsUser(
                     i, serviceState.mConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
@@ -1546,20 +1696,20 @@
 
     private static final class UserState {
         private final int mUserId;
-        // A mapping from the TV IApp ID to its TvIAppState.
-        private Map<String, TvIAppState> mIAppMap = new HashMap<>();
+        // A mapping from the TV Interactive App ID to its TvInteractiveAppState.
+        private Map<String, TvInteractiveAppState> mIAppMap = new HashMap<>();
         // A mapping from the token of a client to its state.
         private final Map<IBinder, ClientState> mClientStateMap = new HashMap<>();
-        // A mapping from the name of a TV IApp service to its state.
+        // A mapping from the name of a TV Interactive App service to its state.
         private final Map<ComponentName, ServiceState> mServiceStateMap = new HashMap<>();
-        // A mapping from the token of a TV IApp session to its state.
+        // A mapping from the token of a TV Interactive App session to its state.
         private final Map<IBinder, SessionState> mSessionStateMap = new HashMap<>();
 
-        // A set of all TV IApp service packages.
+        // A set of all TV Interactive App service packages.
         private final Set<String> mPackageSet = new HashSet<>();
 
         // A list of callbacks.
-        private final RemoteCallbackList<ITvIAppManagerCallback> mCallbacks =
+        private final RemoteCallbackList<ITvInteractiveAppManagerCallback> mCallbacks =
                 new RemoteCallbackList<>();
 
         private UserState(int userId) {
@@ -1567,20 +1717,20 @@
         }
     }
 
-    private static final class TvIAppState {
+    private static final class TvInteractiveAppState {
         private String mIAppServiceId;
         private ComponentName mComponentName;
-        private TvIAppInfo mInfo;
+        private TvInteractiveAppInfo mInfo;
         private int mUid;
         private int mIAppNumber;
     }
 
     private final class SessionState implements IBinder.DeathRecipient {
         private final IBinder mSessionToken;
-        private ITvIAppSession mSession;
+        private ITvInteractiveAppSession mSession;
         private final String mIAppServiceId;
         private final int mType;
-        private final ITvIAppClient mClient;
+        private final ITvInteractiveAppClient mClient;
         private final int mSeq;
         private final ComponentName mComponent;
 
@@ -1595,8 +1745,8 @@
         private final int mUserId;
 
         private SessionState(IBinder sessionToken, String iAppServiceId, int type,
-                ComponentName componentName, ITvIAppClient client, int seq, int callingUid,
-                int callingPid, int userId) {
+                ComponentName componentName, ITvInteractiveAppClient client, int seq,
+                int callingUid, int callingPid, int userId) {
             mSessionToken = sessionToken;
             mIAppServiceId = iAppServiceId;
             mComponent = componentName;
@@ -1660,12 +1810,12 @@
         private final ServiceConnection mConnection;
         private final ComponentName mComponent;
         private final String mIAppServiceId;
-        private final List<Bundle> mPendingAppLinkInfo = new ArrayList<>();
+        private final List<Pair<Bundle, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
         private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
 
         private boolean mPendingPrepare = false;
         private Integer mPendingPrepareType = null;
-        private ITvIAppService mService;
+        private ITvInteractiveAppService mService;
         private ServiceCallback mCallback;
         private boolean mBound;
         private boolean mReconnecting;
@@ -1679,12 +1829,12 @@
             mComponent = component;
             mPendingPrepare = pendingPrepare;
             mPendingPrepareType = prepareType;
-            mConnection = new IAppServiceConnection(component, userId);
+            mConnection = new InteractiveAppServiceConnection(component, userId);
             mIAppServiceId = tias;
         }
 
-        private void addPendingAppLink(Bundle info) {
-            mPendingAppLinkInfo.add(info);
+        private void addPendingAppLink(Bundle info, boolean register) {
+            mPendingAppLinkInfo.add(Pair.create(info, register));
         }
 
         private void addPendingAppLinkCommand(Bundle command) {
@@ -1692,11 +1842,11 @@
         }
     }
 
-    private final class IAppServiceConnection implements ServiceConnection {
+    private final class InteractiveAppServiceConnection implements ServiceConnection {
         private final ComponentName mComponent;
         private final int mUserId;
 
-        private IAppServiceConnection(ComponentName component, int userId) {
+        private InteractiveAppServiceConnection(ComponentName component, int userId) {
             mComponent = component;
             mUserId = userId;
         }
@@ -1714,7 +1864,7 @@
                     return;
                 }
                 ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
-                serviceState.mService = ITvIAppService.Stub.asInterface(service);
+                serviceState.mService = ITvInteractiveAppService.Stub.asInterface(service);
 
                 if (serviceState.mPendingPrepare) {
                     final long identity = Binder.clearCallingIdentity();
@@ -1730,15 +1880,20 @@
                 }
 
                 if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
-                    for (Iterator<Bundle> it = serviceState.mPendingAppLinkInfo.iterator();
+                    for (Iterator<Pair<Bundle, Boolean>> it =
+                            serviceState.mPendingAppLinkInfo.iterator();
                             it.hasNext(); ) {
-                        Bundle appLinkInfo = it.next();
+                        Pair<Bundle, Boolean> appLinkInfoPair = it.next();
                         final long identity = Binder.clearCallingIdentity();
                         try {
-                            serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+                            if (appLinkInfoPair.second) {
+                                serviceState.mService.registerAppLinkInfo(appLinkInfoPair.first);
+                            } else {
+                                serviceState.mService.unregisterAppLinkInfo(appLinkInfoPair.first);
+                            }
                             it.remove();
                         } catch (RemoteException e) {
-                            Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfo
+                            Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfoPair
                                     + ") when onServiceConnected", e);
                         } finally {
                             Binder.restoreCallingIdentity(identity);
@@ -1803,7 +1958,7 @@
         }
     }
 
-    private final class ServiceCallback extends ITvIAppServiceCallback.Stub {
+    private final class ServiceCallback extends ITvInteractiveAppServiceCallback.Stub {
         private final ComponentName mComponent;
         private final int mUserId;
 
@@ -1828,7 +1983,7 @@
         }
     }
 
-    private final class SessionCallback extends ITvIAppSessionCallback.Stub {
+    private final class SessionCallback extends ITvInteractiveAppSessionCallback.Stub {
         private final SessionState mSessionState;
         private final InputChannel[] mInputChannels;
 
@@ -1838,7 +1993,7 @@
         }
 
         @Override
-        public void onSessionCreated(ITvIAppSession session) {
+        public void onSessionCreated(ITvInteractiveAppSession session) {
             if (DEBUG) {
                 Slogf.d(TAG, "onSessionCreated(iAppServiceId="
                         + mSessionState.mIAppServiceId + ")");
@@ -1916,7 +2071,8 @@
         }
 
         @Override
-        public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+        public void onCommandRequest(
+                @TvIAppService.InteractiveAppServiceCommandType String cmdType,
                 Bundle parameters) {
             synchronized (mLock) {
                 if (DEBUG) {
@@ -2020,6 +2176,23 @@
         }
 
         @Override
+        public void onRequestCurrentTvInputId() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onRequestCurrentTvInputId");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onRequestCurrentTvInputId(mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onRequestCurrentTvInputId", e);
+                }
+            }
+        }
+
+        @Override
         public void onAdRequest(AdRequest request) {
             synchronized (mLock) {
                 if (DEBUG) {
@@ -2072,8 +2245,25 @@
             }
         }
 
+        @Override
+        public void onTeletextAppStateChanged(int state) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onTeletextAppStateChanged (state=" + state + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onTeletextAppStateChanged(state, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onTeletextAppStateChanged", e);
+                }
+            }
+        }
+
         @GuardedBy("mLock")
-        private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
+        private boolean addSessionTokenToClientStateLocked(ITvInteractiveAppSession session) {
             try {
                 session.asBinder().linkToDeath(mSessionState, 0);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 6db25b7..c96c1ee 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -22,8 +22,6 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static com.android.server.VcnManagementService.LOCAL_LOG;
 
@@ -47,6 +45,7 @@
 import com.android.server.vcn.VcnContext;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /** @hide */
@@ -121,9 +120,10 @@
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
-        // TODO: Check Network Quality reported by metric monitors/probers.
-
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
+        final boolean isSelectedUnderlyingNetwork =
+                currentlySelected != null
+                        && Objects.equals(currentlySelected.network, networkRecord.network);
 
         final int meteredMatch = networkPriority.getMetered();
         final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
@@ -132,6 +132,23 @@
             return false;
         }
 
+        // Fails bandwidth requirements if either (a) less than exit threshold, or (b), not
+        // selected, but less than entry threshold
+        if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
+                || (caps.getLinkUpstreamBandwidthKbps()
+                                < networkPriority.getMinEntryUpstreamBandwidthKbps()
+                        && !isSelectedUnderlyingNetwork)) {
+            return false;
+        }
+
+        if (caps.getLinkDownstreamBandwidthKbps()
+                        < networkPriority.getMinExitDownstreamBandwidthKbps()
+                || (caps.getLinkDownstreamBandwidthKbps()
+                                < networkPriority.getMinEntryDownstreamBandwidthKbps()
+                        && !isSelectedUnderlyingNetwork)) {
+            return false;
+        }
+
         if (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST)) {
             return true;
         }
@@ -172,8 +189,7 @@
         }
 
         // TODO: Move the Network Quality check to the network metric monitor framework.
-        if (networkPriority.getNetworkQuality()
-                > getWifiQuality(networkRecord, currentlySelected, carrierConfig)) {
+        if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
             return false;
         }
 
@@ -185,7 +201,7 @@
         return true;
     }
 
-    private static int getWifiQuality(
+    private static boolean isWifiRssiAcceptable(
             UnderlyingNetworkRecord networkRecord,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
@@ -196,14 +212,14 @@
 
         if (isSelectedNetwork
                 && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
-            return NETWORK_QUALITY_OK;
+            return true;
         }
 
         if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
-            return NETWORK_QUALITY_OK;
+            return true;
         }
 
-        return NETWORK_QUALITY_ANY;
+        return false;
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 8f703c5..0396a11 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -45,6 +45,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowTracing.WINSCOPE_EXT;
+import static com.android.server.wm.utils.RegionUtils.forEachRect;
 
 import android.accessibilityservice.AccessibilityTrace;
 import android.animation.ObjectAnimator;
@@ -100,7 +101,6 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
 import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
@@ -133,22 +133,19 @@
     private static final Rect EMPTY_RECT = new Rect();
     private static final float[] sTempFloats = new float[9];
 
-    private final SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
-    private final SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
+    private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
+    private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
             new SparseArray<>();
     private SparseArray<IBinder> mFocusedWindow = new SparseArray<>();
     private int mFocusedDisplay = -1;
     private boolean mIsImeVisible = false;
     // Set to true if initializing window population complete.
     private boolean mAllObserversInitialized = true;
-    private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator;
 
     AccessibilityController(WindowManagerService service) {
         mService = service;
         mAccessibilityTracing =
                 AccessibilityController.getAccessibilityControllerInternal(service);
-
-        mAccessibilityWindowsPopulator = new AccessibilityWindowsPopulator(mService, this);
     }
 
     boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
@@ -212,9 +209,7 @@
                 }
                 mWindowsForAccessibilityObserver.remove(displayId);
             }
-            mAccessibilityWindowsPopulator.setWindowsNotification(true);
-            observer = new WindowsForAccessibilityObserver(mService, displayId, callback,
-                    mAccessibilityWindowsPopulator);
+            observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
             mWindowsForAccessibilityObserver.put(displayId, observer);
             mAllObserversInitialized &= observer.mInitialized;
         } else {
@@ -229,10 +224,6 @@
                 }
             }
             mWindowsForAccessibilityObserver.remove(displayId);
-
-            if (mWindowsForAccessibilityObserver.size() <= 0) {
-                mAccessibilityWindowsPopulator.setWindowsNotification(false);
-            }
         }
     }
 
@@ -318,6 +309,11 @@
         if (displayMagnifier != null) {
             displayMagnifier.onDisplaySizeChanged(displayContent);
         }
+        final WindowsForAccessibilityObserver windowsForA11yObserver =
+                mWindowsForAccessibilityObserver.get(displayId);
+        if (windowsForA11yObserver != null) {
+            windowsForA11yObserver.scheduleComputeChangedWindows();
+        }
     }
 
     void onAppWindowTransition(int displayId, int transition) {
@@ -345,6 +341,11 @@
         if (displayMagnifier != null) {
             displayMagnifier.onWindowTransition(windowState, transition);
         }
+        final WindowsForAccessibilityObserver windowsForA11yObserver =
+                mWindowsForAccessibilityObserver.get(displayId);
+        if (windowsForA11yObserver != null) {
+            windowsForA11yObserver.scheduleComputeChangedWindows();
+        }
     }
 
     void onWindowFocusChangedNot(int displayId) {
@@ -454,19 +455,6 @@
         return null;
     }
 
-    boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) {
-        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
-            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay",
-                    FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId);
-        }
-        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
-        if (displayMagnifier == null) {
-            return false;
-        }
-
-        return displayMagnifier.getMagnificationSpec(outSpec);
-    }
-
     boolean hasCallbacks() {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -768,25 +756,6 @@
             return spec;
         }
 
-        boolean getMagnificationSpec(MagnificationSpec outSpec) {
-            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
-                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
-                        FLAGS_MAGNIFICATION_CALLBACK);
-            }
-            MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
-            if (spec == null) {
-                return false;
-            }
-
-            outSpec.setTo(spec);
-            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
-                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
-                        FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}");
-            }
-
-            return true;
-        }
-
         void getMagnificationRegion(Region outMagnificationRegion) {
             if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                 mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
@@ -1434,18 +1403,20 @@
 
         private static final boolean DEBUG = false;
 
-        private final List<AccessibilityWindow> mTempA11yWindows = new ArrayList<>();
+        private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>();
 
         private final Set<IBinder> mTempBinderSet = new ArraySet<>();
 
+        private final RectF mTempRectF = new RectF();
+
+        private final Matrix mTempMatrix = new Matrix();
+
         private final Point mTempPoint = new Point();
 
         private final Region mTempRegion = new Region();
 
         private final Region mTempRegion1 = new Region();
 
-        private final Region mTempRegion2 = new Region();
-
         private final WindowManagerService mService;
 
         private final Handler mHandler;
@@ -1460,11 +1431,10 @@
 
         // Set to true if initializing window population complete.
         private boolean mInitialized;
-        private final AccessibilityWindowsPopulator mA11yWindowsPopulator;
 
         WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
-                int displayId, WindowsForAccessibilityCallback callback,
-                AccessibilityWindowsPopulator accessibilityWindowsPopulator) {
+                int displayId,
+                WindowsForAccessibilityCallback callback) {
             mService = windowManagerService;
             mCallback = callback;
             mDisplayId = displayId;
@@ -1473,7 +1443,6 @@
                     AccessibilityController.getAccessibilityControllerInternal(mService);
             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
                     .getSendRecurringAccessibilityEventsInterval();
-            mA11yWindowsPopulator = accessibilityWindowsPopulator;
             computeChangedWindows(true);
         }
 
@@ -1497,6 +1466,52 @@
             }
         }
 
+        boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
+            int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
+            int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
+                    true);
+            return shellLayer >= wsLayer;
+        }
+
+        int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots,
+                int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows,
+                Region unaccountedSpace, boolean focusedWindowAdded) {
+            while (shellRootIndex < shellRoots.size()
+                    && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) {
+                ShellRoot shellRoot = shellRoots.get(shellRootIndex);
+                shellRootIndex++;
+                final WindowInfo info = shellRoot.getWindowInfo();
+                if (info == null) {
+                    continue;
+                }
+
+                info.layer = addedWindows.size();
+                windows.add(info);
+                addedWindows.add(info.token);
+                unaccountedSpace.op(info.regionInScreen, unaccountedSpace,
+                        Region.Op.REVERSE_DIFFERENCE);
+                if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+                    break;
+                }
+            }
+            return shellRootIndex;
+        }
+
+        private ArrayList<ShellRoot> getSortedShellRoots(
+                SparseArray<ShellRoot> originalShellRoots) {
+            ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size());
+            for (int i = originalShellRoots.size() - 1; i >= 0; --i) {
+                sortedShellRoots.add(originalShellRoots.valueAt(i));
+            }
+
+            sortedShellRoots.sort((left, right) ->
+                    mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true)
+                            - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(),
+                            true));
+
+            return sortedShellRoots;
+        }
+
         /**
          * Check if windows have changed, and send them to the accessibility subsystem if they have.
          *
@@ -1546,29 +1561,44 @@
                 Region unaccountedSpace = mTempRegion;
                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
 
-                final List<AccessibilityWindow> visibleWindows = mTempA11yWindows;
-                mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
-                        mDisplayId, visibleWindows);
+                final SparseArray<WindowState> visibleWindows = mTempWindowStates;
+                populateVisibleWindowsOnScreen(visibleWindows);
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
                 boolean focusedWindowAdded = false;
 
                 final int visibleWindowCount = visibleWindows.size();
+                ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>();
+
+                ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
 
                 // Iterate until we figure out what is touchable for the entire screen.
-                for (int i = 0; i < visibleWindowCount; i++) {
-                    final AccessibilityWindow a11yWindow = visibleWindows.get(i);
-                    final Region regionInWindow = new Region();
-                    a11yWindow.getTouchableRegionInWindow(regionInWindow);
-                    if (windowMattersToAccessibility(a11yWindow, regionInWindow,
-                            unaccountedSpace)) {
-                        addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
-                        if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
-                            updateUnaccountedSpace(a11yWindow, unaccountedSpace);
+                int shellRootIndex = 0;
+                for (int i = visibleWindowCount - 1; i >= 0; i--) {
+                    final WindowState windowState = visibleWindows.valueAt(i);
+                    int prevShellRootIndex = shellRootIndex;
+                    shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex,
+                            windows, addedWindows, unaccountedSpace, focusedWindowAdded);
+
+                    // If a Shell Root was added, it could have accounted for all the space already.
+                    if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty()
+                            && focusedWindowAdded) {
+                        break;
+                    }
+
+                    final Region regionInScreen = new Region();
+                    computeWindowRegionInScreen(windowState, regionInScreen);
+                    if (windowMattersToAccessibility(windowState,
+                            regionInScreen, unaccountedSpace,
+                            skipRemainingWindowsForTaskFragments)) {
+                        addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows);
+                        if (windowMattersToUnaccountedSpaceComputation(windowState)) {
+                            updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
+                                    skipRemainingWindowsForTaskFragments);
                         }
-                        focusedWindowAdded |= a11yWindow.isFocused();
-                    } else if (a11yWindow.isUntouchableNavigationBar()) {
+                        focusedWindowAdded |= windowState.isFocused();
+                    } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
                         // If this widow is navigation bar without touchable region, accounting the
                         // region of navigation bar inset because all touch events from this region
                         // would be received by launcher, i.e. this region is a un-touchable one
@@ -1617,39 +1647,47 @@
 
         // Some windows should be excluded from unaccounted space computation, though they still
         // should be reported
-        private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
+        private boolean windowMattersToUnaccountedSpaceComputation(WindowState windowState) {
             // Do not account space of trusted non-touchable windows, except the split-screen
             // divider.
             // If it's not trusted, touch events are not sent to the windows behind it.
-            if (((a11yWindow.getFlags() & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                    && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
-                    && a11yWindow.isTrustedOverlay()) {
+            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)
+                    && windowState.isTrustedOverlay()) {
                 return false;
             }
 
-            if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+            if (windowState.mAttrs.type
+                    == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
                 return false;
             }
             return true;
         }
 
-        private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
-                Region regionInScreen, Region unaccountedSpace) {
-            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
+        private boolean windowMattersToAccessibility(WindowState windowState,
+                Region regionInScreen, Region unaccountedSpace,
+                ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
+            final RecentsAnimationController controller = mService.getRecentsAnimationController();
+            if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
                 return false;
             }
 
-            if (a11yWindow.isFocused()) {
+            if (windowState.isFocused()) {
                 return true;
             }
 
+            // If the window is part of a task that we're finished with - ignore.
+            final TaskFragment taskFragment = windowState.getTaskFragment();
+            if (taskFragment != null
+                    && skipRemainingWindowsForTaskFragments.contains(taskFragment)) {
+                return false;
+            }
+
             // Ignore non-touchable windows, except the split-screen divider, which is
             // occasionally non-touchable but still useful for identifying split-screen
-            // mode and the PIP menu.
-            if (((a11yWindow.getFlags()
-                    & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                    && (a11yWindow.getType() != TYPE_DOCK_DIVIDER
-                    && !a11yWindow.isPIPMenu())) {
+            // mode.
+            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
                 return false;
             }
 
@@ -1659,36 +1697,88 @@
             }
 
             // Add windows of certain types not covered by modal windows.
-            if (isReportedWindowType(a11yWindow.getType())) {
+            if (isReportedWindowType(windowState.mAttrs.type)) {
                 return true;
             }
 
             return false;
         }
 
-        private void updateUnaccountedSpace(AccessibilityWindow a11yWindow,
-                Region unaccountedSpace) {
-            if (a11yWindow.getType()
-                    != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
-                // Account for the space this window takes if the window
-                // is not an accessibility overlay which does not change
-                // the reported windows.
-                final Region touchableRegion = mTempRegion2;
-                a11yWindow.getTouchableRegionInScreen(touchableRegion);
-                unaccountedSpace.op(touchableRegion, unaccountedSpace,
-                        Region.Op.REVERSE_DIFFERENCE);
-                // Account for the space of letterbox.
-                final Region letterboxBounds = mTempRegion1;
-                if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) {
-                    unaccountedSpace.op(letterboxBounds,
-                            unaccountedSpace, Region.Op.REVERSE_DIFFERENCE);
+        private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
+                Region unaccountedSpace,
+                ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
+            // Account for the space this window takes if the window
+            // is not an accessibility overlay which does not change
+            // the reported windows.
+            unaccountedSpace.op(regionInScreen, unaccountedSpace,
+                    Region.Op.REVERSE_DIFFERENCE);
+
+            // If a window is modal it prevents other windows from being touched
+            if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+                if (!windowState.hasTapExcludeRegion()) {
+                    // Account for all space in the task, whether the windows in it are
+                    // touchable or not. The modal window blocks all touches from the task's
+                    // area.
+                    unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
+                } else {
+                    // If a window has tap exclude region, we need to account it.
+                    final Region displayRegion = new Region(windowState.getDisplayFrame());
+                    final Region tapExcludeRegion = new Region();
+                    windowState.getTapExcludeRegion(tapExcludeRegion);
+                    displayRegion.op(tapExcludeRegion, displayRegion,
+                            Region.Op.REVERSE_DIFFERENCE);
+                    unaccountedSpace.op(displayRegion, unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
                 }
+
+                final TaskFragment taskFragment = windowState.getTaskFragment();
+                if (taskFragment != null) {
+                    // If the window is associated with a particular task, we can skip the
+                    // rest of the windows for that task.
+                    skipRemainingWindowsForTaskFragments.add(taskFragment);
+                } else if (!windowState.hasTapExcludeRegion()) {
+                    // If the window is not associated with a particular task, then it is
+                    // globally modal. In this case we can skip all remaining windows when
+                    // it doesn't has tap exclude region.
+                    unaccountedSpace.setEmpty();
+                }
+            }
+
+            // Account for the space of letterbox.
+            if (windowState.areAppWindowBoundsLetterboxed()) {
+                unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace,
+                        Region.Op.REVERSE_DIFFERENCE);
             }
         }
 
-        private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow,
-                Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) {
-            final WindowInfo window = a11yWindow.getWindowInfo();
+        private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) {
+            // Get the touchable frame.
+            Region touchableRegion = mTempRegion1;
+            windowState.getTouchableRegion(touchableRegion);
+
+            // Map the frame to get what appears on the screen.
+            Matrix matrix = mTempMatrix;
+            populateTransformationMatrix(windowState, matrix);
+
+            forEachRect(touchableRegion, rect -> {
+                // Move to origin as all transforms are captured by the matrix.
+                RectF windowFrame = mTempRectF;
+                windowFrame.set(rect);
+                windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top);
+
+                matrix.mapRect(windowFrame);
+
+                // Union all rects.
+                outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
+                        (int) windowFrame.right, (int) windowFrame.bottom));
+            });
+        }
+
+        private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen,
+                List<WindowInfo> out, Set<IBinder> tokenOut) {
+            final WindowInfo window = windowState.getWindowInfo();
             window.regionInScreen.set(regionInScreen);
             window.layer = tokenOut.size();
             out.add(window);
@@ -1715,6 +1805,23 @@
                     && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
         }
 
+        private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) {
+            final List<WindowState> tempWindowStatesList = new ArrayList<>();
+            final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+            if (dc == null) {
+                return;
+            }
+
+            dc.forAllWindows(w -> {
+                if (w.isVisible()) {
+                    tempWindowStatesList.add(w);
+                }
+            }, false /* traverseTopToBottom */);
+            for (int i = 0; i < tempWindowStatesList.size(); i++) {
+                outWindows.put(i, tempWindowStatesList.get(i));
+            }
+        }
+
         private WindowState getTopFocusWindow() {
             return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
         }
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
deleted file mode 100644
index f31ae06..0000000
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-
-import static com.android.server.wm.utils.RegionUtils.forEachRect;
-
-import android.annotation.NonNull;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.IWindow;
-import android.view.InputWindowHandle;
-import android.view.MagnificationSpec;
-import android.view.WindowInfo;
-import android.view.WindowManager;
-import android.window.WindowInfosListener;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is the accessibility windows population adapter.
- */
-public final class AccessibilityWindowsPopulator extends WindowInfosListener {
-
-    private static final String TAG = AccessibilityWindowsPopulator.class.getSimpleName();
-    // If the surface flinger callback is not coming within in 2 frames time, i.e. about
-    // 35ms, then assuming the windows become stable.
-    private static final int SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS = 35;
-    // To avoid the surface flinger callbacks always comes within in 2 frames, then no windows
-    // are reported to the A11y framework, and the animation duration time is 500ms, so setting
-    // this value as the max timeout value to force computing changed windows.
-    private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 500;
-
-    private static final float[] sTempFloats = new float[9];
-
-    private final WindowManagerService mService;
-    private final AccessibilityController mAccessibilityController;
-    @GuardedBy("mLock")
-    private final SparseArray<List<InputWindowHandle>> mInputWindowHandlesOnDisplays =
-            new SparseArray<>();
-    @GuardedBy("mLock")
-    private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>();
-    @GuardedBy("mLock")
-    private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>();
-    @GuardedBy("mLock")
-    private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>();
-    @GuardedBy("mLock")
-    private boolean mWindowsNotificationEnabled = false;
-    private final Object mLock = new Object();
-    private final Handler mHandler;
-
-    AccessibilityWindowsPopulator(WindowManagerService service,
-            AccessibilityController accessibilityController) {
-        mService = service;
-        mAccessibilityController = accessibilityController;
-        mHandler = new MyHandler(mService.mH.getLooper());
-
-        register();
-    }
-
-    /**
-     * Gets the visible windows list with the window layer on the specified display.
-     *
-     * @param displayId The display.
-     * @param outWindows The visible windows list. The z-order of each window in the list
-     *                   is from the top to bottom.
-     */
-    public void populateVisibleWindowsOnScreenLocked(int displayId,
-            List<AccessibilityWindow> outWindows) {
-        List<InputWindowHandle> inputWindowHandles;
-        final Matrix inverseMatrix = new Matrix();
-        final Matrix displayMatrix = new Matrix();
-
-        synchronized (mLock) {
-            inputWindowHandles = mInputWindowHandlesOnDisplays.get(displayId);
-            if (inputWindowHandles == null) {
-                outWindows.clear();
-
-                return;
-            }
-            inverseMatrix.set(mMagnificationSpecInverseMatrix.get(displayId));
-
-            final DisplayInfo displayInfo = mDisplayInfos.get(displayId);
-            if (displayInfo != null) {
-                displayMatrix.set(displayInfo.mTransform);
-            } else {
-                Slog.w(TAG, "The displayInfo of this displayId (" + displayId + ") called "
-                        + "back from the surface fligner is null");
-            }
-        }
-
-        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-        final ShellRoot shellroot = dc.mShellRoots.get(WindowManager.SHELL_ROOT_LAYER_PIP);
-        final IBinder pipMenuIBinder =
-                shellroot != null ? shellroot.getAccessibilityWindowToken() : null;
-
-        for (final InputWindowHandle windowHandle : inputWindowHandles) {
-            final AccessibilityWindow accessibilityWindow =
-                    AccessibilityWindow.initializeData(mService, windowHandle, inverseMatrix,
-                            pipMenuIBinder, displayMatrix);
-
-            outWindows.add(accessibilityWindow);
-        }
-    }
-
-    @Override
-    public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
-            DisplayInfo[] displayInfos) {
-        synchronized (mLock) {
-            mVisibleWindows.clear();
-            for (InputWindowHandle window : windowHandles) {
-                if (window.visible && window.getWindow() != null) {
-                    mVisibleWindows.add(window);
-                }
-            }
-
-            mDisplayInfos.clear();
-            for (final DisplayInfo displayInfo : displayInfos) {
-                mDisplayInfos.put(displayInfo.mDisplayId, displayInfo);
-            }
-
-            if (mWindowsNotificationEnabled) {
-                if (!mHandler.hasMessages(
-                        MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
-                    mHandler.sendEmptyMessageDelayed(
-                            MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
-                            WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
-                }
-                populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
-            }
-        }
-    }
-
-    /**
-     * Sets to notify the accessibilityController to compute changed windows on
-     * the display after populating the visible windows if the windows reported
-     * from the surface flinger changes.
-     *
-     * @param register {@code true} means starting windows population.
-     */
-    public void setWindowsNotification(boolean register) {
-        synchronized (mLock) {
-            if (mWindowsNotificationEnabled == register) {
-                return;
-            }
-            mWindowsNotificationEnabled = register;
-            if (mWindowsNotificationEnabled) {
-                populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
-            } else {
-                releaseResources();
-            }
-        }
-    }
-
-    private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() {
-        final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>();
-
-        for (final InputWindowHandle windowHandle : mVisibleWindows) {
-            List<InputWindowHandle> inputWindowHandles = tempWindowHandleList.get(
-                    windowHandle.displayId);
-
-            if (inputWindowHandles == null) {
-                inputWindowHandles = new ArrayList<>();
-                tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles);
-                generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId);
-            }
-            inputWindowHandles.add(windowHandle);
-        }
-
-        final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
-
-        getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList,
-                mInputWindowHandlesOnDisplays);
-        // Clones all windows from the callback of the surface flinger.
-        mInputWindowHandlesOnDisplays.clear();
-        for (int i = 0; i < tempWindowHandleList.size(); i++) {
-            final int displayId = tempWindowHandleList.keyAt(i);
-            mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId));
-        }
-
-        if (displayIdsForWindowsChanged.size() > 0) {
-            if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) {
-                mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
-                        displayIdsForWindowsChanged).sendToTarget();
-            }
-
-            return;
-        }
-        mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE);
-        mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE,
-                SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS);
-    }
-
-    private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged,
-            SparseArray<List<InputWindowHandle>> newWindowsList,
-            SparseArray<List<InputWindowHandle>> oldWindowsList) {
-        for (int i = 0; i < newWindowsList.size(); i++) {
-            final int displayId = newWindowsList.keyAt(i);
-            final List<InputWindowHandle> newWindows = newWindowsList.get(displayId);
-            final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId);
-
-            if (hasWindowsChangedLocked(newWindows, oldWindows)) {
-                outDisplayIdsForWindowsChanged.add(displayId);
-            }
-        }
-    }
-
-    private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows,
-            List<InputWindowHandle> oldWindows) {
-        if (oldWindows == null || oldWindows.size() != newWindows.size()) {
-            return true;
-        }
-
-        final int windowsCount = newWindows.size();
-        // Since we always traverse windows from high to low layer,
-        // the old and new windows at the same index should be the
-        // same, otherwise something changed.
-        for (int i = 0; i < windowsCount; i++) {
-            final InputWindowHandle newWindow = newWindows.get(i);
-            final InputWindowHandle oldWindow = oldWindows.get(i);
-
-            if (!newWindow.getWindow().asBinder().equals(oldWindow.getWindow().asBinder())) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private void generateMagnificationSpecInverseMatrixLocked(int displayId) {
-        MagnificationSpec spec = new MagnificationSpec();
-        if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) {
-            return;
-        }
-        sTempFloats[Matrix.MSCALE_X] = spec.scale;
-        sTempFloats[Matrix.MSKEW_Y] = 0;
-        sTempFloats[Matrix.MSKEW_X] = 0;
-        sTempFloats[Matrix.MSCALE_Y] = spec.scale;
-        sTempFloats[Matrix.MTRANS_X] = spec.offsetX;
-        sTempFloats[Matrix.MTRANS_Y] = spec.offsetY;
-        sTempFloats[Matrix.MPERSP_0] = 0;
-        sTempFloats[Matrix.MPERSP_1] = 0;
-        sTempFloats[Matrix.MPERSP_2] = 1;
-
-        final Matrix tempMatrix = new Matrix();
-        tempMatrix.setValues(sTempFloats);
-
-        final Matrix inverseMatrix = new Matrix();
-        final boolean result = tempMatrix.invert(inverseMatrix);
-
-        if (!result) {
-            Slog.e(TAG, "Can't inverse the magnification spec matrix with the "
-                    + "magnification spec = " + spec + " on the displayId = " + displayId);
-            return;
-        }
-        mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix);
-    }
-
-    private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) {
-        mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT);
-
-        for (int i = 0; i < displayIdsForWindowsChanged.size(); i++) {
-            mAccessibilityController.performComputeChangedWindowsNot(
-                    displayIdsForWindowsChanged.get(i), false);
-        }
-    }
-
-    private void forceUpdateWindows() {
-        final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
-
-        synchronized (mLock) {
-            for (int i = 0; i < mInputWindowHandlesOnDisplays.size(); i++) {
-                final int displayId = mInputWindowHandlesOnDisplays.keyAt(i);
-                displayIdsForWindowsChanged.add(displayId);
-            }
-        }
-        notifyWindowsChanged(displayIdsForWindowsChanged);
-    }
-
-    @GuardedBy("mLock")
-    private void releaseResources() {
-        mInputWindowHandlesOnDisplays.clear();
-        mMagnificationSpecInverseMatrix.clear();
-        mVisibleWindows.clear();
-        mDisplayInfos.clear();
-        mWindowsNotificationEnabled = false;
-        mHandler.removeCallbacksAndMessages(null);
-    }
-
-    private class MyHandler extends Handler {
-        public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
-        public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE = 2;
-        public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT = 3;
-
-        MyHandler(Looper looper) {
-            super(looper, null, false);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
-                    final List<Integer> displayIdsForWindowsChanged = (List<Integer>) message.obj;
-                    notifyWindowsChanged(displayIdsForWindowsChanged);
-                } break;
-
-                case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE: {
-                    forceUpdateWindows();
-                } break;
-
-                case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT: {
-                    Slog.w(TAG, "Windows change within in 2 frames continuously over 500 ms "
-                            + "and notify windows changed immediately");
-                    mHandler.removeMessages(
-                            MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE);
-
-                    forceUpdateWindows();
-                } break;
-            }
-        }
-    }
-
-    /**
-     * This class represents information about a window from the
-     * surface flinger to the accessibility framework.
-     */
-    public static class AccessibilityWindow {
-        private static final Region TEMP_REGION = new Region();
-        private static final RectF TEMP_RECTF = new RectF();
-        // Data
-        private IWindow mWindow;
-        private int mDisplayId;
-        private int mFlags;
-        private int mType;
-        private int mPrivateFlags;
-        private boolean mIsPIPMenu;
-        private boolean mIsFocused;
-        private boolean mShouldMagnify;
-        private boolean mIgnoreDuetoRecentsAnimation;
-        private boolean mIsTrustedOverlay;
-        private final Region mTouchableRegionInScreen = new Region();
-        private final Region mTouchableRegionInWindow = new Region();
-        private final Region mLetterBoxBounds = new Region();
-        private WindowInfo mWindowInfo;
-
-        /**
-         * Returns the instance after initializing the internal data.
-         * @param service The window manager service.
-         * @param inputWindowHandle The window from the surface flinger.
-         * @param inverseMatrix The magnification spec inverse matrix.
-         */
-        public static AccessibilityWindow initializeData(WindowManagerService service,
-                InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder,
-                Matrix displayMatrix) {
-            final IWindow window = inputWindowHandle.getWindow();
-            final WindowState windowState = window != null ? service.mWindowMap.get(
-                    window.asBinder()) : null;
-
-            final AccessibilityWindow instance = new AccessibilityWindow();
-
-            instance.mWindow = inputWindowHandle.getWindow();
-            instance.mDisplayId = inputWindowHandle.displayId;
-            instance.mFlags = inputWindowHandle.layoutParamsFlags;
-            instance.mType = inputWindowHandle.layoutParamsType;
-            instance.mIsPIPMenu = inputWindowHandle.getWindow().asBinder().equals(pipIBinder);
-
-            // TODO (b/199357848): gets the private flag of the window from other way.
-            instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0;
-            // TODO (b/199358208) : using new way to implement the focused window.
-            instance.mIsFocused = windowState != null && windowState.isFocused();
-            instance.mShouldMagnify = windowState == null || windowState.shouldMagnify();
-
-            final RecentsAnimationController controller = service.getRecentsAnimationController();
-            instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
-                    && controller.shouldIgnoreForAccessibility(windowState);
-            instance.mIsTrustedOverlay = inputWindowHandle.trustedOverlay;
-
-            // TODO (b/199358388) : gets the letterbox bounds of the window from other way.
-            if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) {
-                getLetterBoxBounds(windowState, instance.mLetterBoxBounds);
-            }
-
-            final Rect windowFrame = new Rect(inputWindowHandle.frameLeft,
-                    inputWindowHandle.frameTop, inputWindowHandle.frameRight,
-                    inputWindowHandle.frameBottom);
-            getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion,
-                    instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix);
-            getUnMagnifiedTouchableRegion(instance.mShouldMagnify,
-                    inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen,
-                    inverseMatrix, displayMatrix);
-            instance.mWindowInfo = windowState != null
-                    ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance);
-
-            return instance;
-        }
-
-        /**
-         * Returns the touchable region in the screen.
-         * @param outRegion The touchable region.
-         */
-        public void getTouchableRegionInScreen(Region outRegion) {
-            outRegion.set(mTouchableRegionInScreen);
-        }
-
-        /**
-         * Returns the touchable region in the window.
-         * @param outRegion The touchable region.
-         */
-        public void getTouchableRegionInWindow(Region outRegion) {
-            outRegion.set(mTouchableRegionInWindow);
-        }
-
-        /**
-         * @return the layout parameter flag {@link android.view.WindowManager.LayoutParams#flags}.
-         */
-        public int getFlags() {
-            return mFlags;
-        }
-
-        /**
-         * @return the layout parameter type {@link android.view.WindowManager.LayoutParams#type}.
-         */
-        public int getType() {
-            return mType;
-        }
-
-        /**
-         * @return the layout parameter private flag
-         * {@link android.view.WindowManager.LayoutParams#privateFlags}.
-         */
-        public int getPrivateFlag() {
-            return mPrivateFlags;
-        }
-
-        /**
-         * @return the windowInfo {@link WindowInfo}.
-         */
-        public WindowInfo getWindowInfo() {
-            return mWindowInfo;
-        }
-
-        /**
-         * Gets the letter box bounds if activity bounds are letterboxed
-         * or letterboxed for display cutout.
-         *
-         * @return {@code true} there's a letter box bounds.
-         */
-        public Boolean setLetterBoxBoundsIfNeeded(Region outBounds) {
-            if (mLetterBoxBounds.isEmpty()) {
-                return false;
-            }
-
-            outBounds.set(mLetterBoxBounds);
-            return true;
-        }
-
-        /**
-         * @return true if this window should be magnified.
-         */
-        public boolean shouldMagnify() {
-            return mShouldMagnify;
-        }
-
-        /**
-         * @return true if this window is focused.
-         */
-        public boolean isFocused() {
-            return mIsFocused;
-        }
-
-        /**
-         * @return true if it's running the recent animation but not the target app.
-         */
-        public boolean ignoreRecentsAnimationForAccessibility() {
-            return mIgnoreDuetoRecentsAnimation;
-        }
-
-        /**
-         * @return true if this window is the trusted overlay.
-         */
-        public boolean isTrustedOverlay() {
-            return mIsTrustedOverlay;
-        }
-
-        /**
-         * @return true if this window is the navigation bar with the gesture mode.
-         */
-        public boolean isUntouchableNavigationBar() {
-            if (mType != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
-                return false;
-            }
-
-            return mTouchableRegionInScreen.isEmpty();
-        }
-
-        /**
-         * @return true if this window is PIP menu.
-         */
-        public boolean isPIPMenu() {
-            return mIsPIPMenu;
-        }
-
-        private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion,
-                Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) {
-            // Some modal windows, like the activity with Theme.dialog, has the full screen
-            // as its touchable region, but its window frame is smaller than the touchable
-            // region. The region we report should be the touchable area in the window frame
-            // for the consistency and match developers expectation.
-            // So we need to make the intersection between the frame and touchable region to
-            // obtain the real touch region in the screen.
-            Region touchRegion = TEMP_REGION;
-            touchRegion.set(inRegion);
-            touchRegion.op(frame, Region.Op.INTERSECT);
-
-            getUnMagnifiedTouchableRegion(shouldMagnify, touchRegion, outRegion, inverseMatrix,
-                    displayMatrix);
-        }
-
-        /**
-         * Gets the un-magnified touchable region. If this window can be magnified and magnifying,
-         * we will transform the input touchable region by applying the inverse matrix of the
-         * magnification spec to get the un-magnified touchable region.
-         * @param shouldMagnify The window can be magnified.
-         * @param inRegion The touchable region of this window.
-         * @param outRegion The un-magnified touchable region of this window.
-         * @param inverseMatrix The inverse matrix of the magnification spec.
-         * @param displayMatrix The display transform matrix which takes display coordinates to
-         *                      logical display coordinates.
-         */
-        private static void getUnMagnifiedTouchableRegion(boolean shouldMagnify, Region inRegion,
-                Region outRegion, Matrix inverseMatrix, Matrix displayMatrix) {
-            if ((!shouldMagnify || inverseMatrix.isIdentity()) && displayMatrix.isIdentity()) {
-                outRegion.set(inRegion);
-                return;
-            }
-
-            forEachRect(inRegion, rect -> {
-                // Move to origin as all transforms are captured by the matrix.
-                RectF windowFrame = TEMP_RECTF;
-                windowFrame.set(rect);
-
-                inverseMatrix.mapRect(windowFrame);
-                displayMatrix.mapRect(windowFrame);
-                // Union all rects.
-                outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
-                        (int) windowFrame.right, (int) windowFrame.bottom));
-            });
-        }
-
-        private static WindowInfo getWindowInfoForWindowlessWindows(AccessibilityWindow window) {
-            WindowInfo windowInfo = WindowInfo.obtain();
-            windowInfo.displayId = window.mDisplayId;
-            windowInfo.type = window.mType;
-            windowInfo.token = window.mWindow.asBinder();
-            windowInfo.hasFlagWatchOutsideTouch = (window.mFlags
-                    & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
-            windowInfo.inPictureInPicture = false;
-
-            // There only are two windowless windows now, one is split window, and the other
-            // one is PIP.
-            if (windowInfo.type == TYPE_DOCK_DIVIDER) {
-                windowInfo.title = "Splitscreen Divider";
-            } else if (window.mIsPIPMenu) {
-                windowInfo.title = "Picture-in-Picture menu";
-            }
-            return windowInfo;
-        }
-
-        private static void getLetterBoxBounds(WindowState windowState, Region outRegion) {
-            final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets();
-            final Rect nonLetterboxRect = windowState.getBounds();
-
-            nonLetterboxRect.inset(letterboxInsets);
-            outRegion.set(windowState.getBounds());
-            outRegion.op(nonLetterboxRect, Region.Op.DIFFERENCE);
-        }
-
-        @Override
-        public String toString() {
-            String builder = "A11yWindow=[" + mWindow.asBinder()
-                    + ", displayId=" + mDisplayId
-                    + ", flag=0x" + Integer.toHexString(mFlags)
-                    + ", type=" + mType
-                    + ", privateFlag=0x" + Integer.toHexString(mPrivateFlags)
-                    + ", focused=" + mIsFocused
-                    + ", shouldMagnify=" + mShouldMagnify
-                    + ", ignoreDuetoRecentsAnimation=" + mIgnoreDuetoRecentsAnimation
-                    + ", isTrustedOverlay=" + mIsTrustedOverlay
-                    + ", regionInScreen=" + mTouchableRegionInScreen
-                    + ", touchableRegion=" + mTouchableRegionInWindow
-                    + ", letterBoxBounds=" + mLetterBoxBounds
-                    + ", isPIPMenu=" + mIsPIPMenu
-                    + ", windowInfo=" + mWindowInfo
-                    + "]";
-
-            return builder;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 1bb9ca7..48dd2f4 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -55,6 +55,7 @@
             FIRST_ORDERED_ID,
             COMMUNAL_MODE_ORDERED_ID,
             PERMISSION_POLICY_ORDERED_ID,
+            VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
             LAST_ORDERED_ID // Update this when adding new ids
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -76,10 +77,16 @@
     public static final int PERMISSION_POLICY_ORDERED_ID = 2;
 
     /**
+     * The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
+     * interceptor.
+     */
+    public static final int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3;
+
+    /**
      * The final id, used by the framework to determine the valid range of ids. Update this when
      * adding new ids.
      */
-    static final int LAST_ORDERED_ID = PERMISSION_POLICY_ORDERED_ID;
+    static final int LAST_ORDERED_ID = VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
 
     /**
      * Data class for storing the various arguments needed for activity interception.
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 4b33f0e..a8dd856 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -11,8 +11,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START;
@@ -483,10 +481,6 @@
             case WINDOWING_MODE_FULLSCREEN:
                 mWindowState = WINDOW_STATE_STANDARD;
                 break;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
-                break;
             case WINDOWING_MODE_FREEFORM:
                 mWindowState = WINDOW_STATE_FREEFORM;
                 break;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c929cbb..c2765db 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1546,10 +1546,17 @@
     void onDisplayChanged(DisplayContent dc) {
         DisplayContent prevDc = mDisplayContent;
         super.onDisplayChanged(dc);
-        if (prevDc == null || prevDc == mDisplayContent) {
+        if (prevDc == mDisplayContent) {
             return;
         }
 
+        mDisplayContent.onRunningActivityChanged();
+
+        if (prevDc == null) {
+            return;
+        }
+        prevDc.onRunningActivityChanged();
+
         // TODO(b/169035022): move to a more-appropriate place.
         mTransitionController.collect(this);
         if (prevDc.mOpeningApps.remove(this)) {
@@ -1623,6 +1630,11 @@
         }
         // Trigger TaskInfoChanged to update the camera compat UI.
         getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+        // TaskOrganizerController#onTaskInfoChanged adds pending task events to the queue waiting
+        // for the surface placement to be ready. So need to trigger surface placement to dispatch
+        // events to avoid stale state for the camera compat control.
+        getDisplayContent().setLayoutNeeded();
+        mWmService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
     void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
@@ -3900,6 +3912,7 @@
 
         // Reset the last saved PiP snap fraction on removal.
         mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent);
+        mDisplayContent.onRunningActivityChanged();
         mWmService.mEmbeddedWindowController.onActivityRemoved(this);
         mRemovingFromDisplay = false;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 9353f6d..1681348 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.InputConstants;
 import android.os.Looper;
+import android.os.Process;
 import android.util.Slog;
 import android.view.InputChannel;
 import android.view.InputEvent;
@@ -113,14 +114,13 @@
     }
 
     private InputWindowHandle createInputWindowHandle() {
-        InputWindowHandle inputWindowHandle = new InputWindowHandle(
-                mActivityRecord.getInputApplicationHandle(false),
+        InputWindowHandle inputWindowHandle = new InputWindowHandle(null,
                 mActivityRecord.getDisplayId());
         inputWindowHandle.replaceTouchableRegionWithCrop(
                 mActivityRecord.getParentSurfaceControl());
         inputWindowHandle.name = mName;
-        inputWindowHandle.ownerUid = mActivityRecord.getUid();
-        inputWindowHandle.ownerPid = mActivityRecord.getPid();
+        inputWindowHandle.ownerUid = Process.myUid();
+        inputWindowHandle.ownerPid = Process.myPid();
         inputWindowHandle.layoutParamsFlags =
                 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 65f9e83..e119a9a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,8 +30,6 @@
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
@@ -1926,27 +1924,6 @@
 
     private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
             Task targetTask) {
-        final Task sourceRootTask = mSourceRootTask != null ? mSourceRootTask
-                : mRootWindowContainer.getTopDisplayFocusedRootTask();
-        if (sourceRootTask != null && sourceRootTask.inSplitScreenWindowingMode()
-                && (mOptions == null
-                        || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) {
-            int windowingMode =
-                    targetTask != null ? targetTask.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-            if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
-                if (sourceRootTask.inSplitScreenPrimaryWindowingMode()) {
-                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-                } else if (sourceRootTask.inSplitScreenSecondaryWindowingMode()) {
-                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-                }
-            }
-
-            if (mOptions == null) {
-                mOptions = ActivityOptions.makeBasic();
-            }
-            mOptions.setLaunchWindowingMode(windowingMode);
-        }
-
         mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
                 sourceRecord, mOptions, mRequest, PHASE_BOUNDS, mLaunchParams);
         mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3cecce2..dd394ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -36,7 +36,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -364,6 +363,17 @@
     /** Check if placing task or activity on specified display is allowed. */
     boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
             ActivityInfo activityInfo) {
+        return canPlaceEntityOnDisplay(displayId, callingPid, callingUid, null /* task */,
+                activityInfo);
+    }
+
+    boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid, Task task) {
+        return canPlaceEntityOnDisplay(displayId, callingPid, callingUid, task,
+                null /* activityInfo */);
+    }
+
+    private boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+            Task task, ActivityInfo activityInfo) {
         if (displayId == DEFAULT_DISPLAY) {
             // No restrictions for the default display.
             return true;
@@ -372,12 +382,31 @@
             // Can't launch on secondary displays if feature is not supported.
             return false;
         }
+
         if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
             // Can't place activities to a display that has restricted launch rules.
             // In this case the request should be made by explicitly adding target display id and
             // by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
             return false;
         }
+
+        final DisplayContent displayContent =
+                mRootWindowContainer.getDisplayContentOrCreate(displayId);
+        if (displayContent != null && displayContent.mDwpcHelper.hasController()) {
+            final ArrayList<ActivityInfo> activities = new ArrayList<>();
+            if (activityInfo != null) {
+                activities.add(activityInfo);
+            }
+            if (task != null) {
+                task.forAllActivities((r) -> {
+                    activities.add(r.info);
+                });
+            }
+            if (!displayContent.mDwpcHelper.canContainActivities(activities)) {
+                return false;
+            }
+        }
+
         return true;
     }
 
@@ -2169,10 +2198,7 @@
             boolean forceNonResizable) {
         final boolean isSecondaryDisplayPreferred = preferredTaskDisplayArea != null
                 && preferredTaskDisplayArea.getDisplayId() != DEFAULT_DISPLAY;
-        final boolean inSplitScreenMode = actualRootTask != null
-                && actualRootTask.getDisplayArea().isSplitScreenModeActivated();
-        if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
-                && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
+        if (!task.isActivityTypeStandardOrUndefined()) {
             return;
         }
 
@@ -2513,10 +2539,8 @@
                                 == PERMISSION_GRANTED) {
                     mRecentTasks.setFreezeTaskListReordering();
                 }
-                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                        || activityOptions.getLaunchRootTask() != null) {
-                    // Don't move home activity forward if we are launching into primary split or
-                    // there is a launch root set.
+                if (activityOptions.getLaunchRootTask() != null) {
+                    // Don't move home activity forward if there is a launch root set.
                     moveHomeTaskForward = false;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 217fc09..d91f48e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -222,7 +222,6 @@
 import android.view.WindowManager;
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.DisplayWindowPolicyController;
 import android.window.IDisplayAreaOrganizer;
 import android.window.TransitionRequestInfo;
 
@@ -698,12 +697,11 @@
     boolean mDontMoveToTop;
 
     /**
-     * The policy controller of the windows that can be displayed on the virtual display.
+     * The helper of policy controller.
      *
-     * @see DisplayWindowPolicyController
+     * @see DisplayWindowPolicyControllerHelper
      */
-    @Nullable
-    DisplayWindowPolicyController mDisplayWindowPolicyController;
+    DisplayWindowPolicyControllerHelper mDwpcHelper;
 
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -2739,8 +2737,7 @@
                 mDisplayInfo.copyFrom(newDisplayInfo);
             }
 
-            mDisplayWindowPolicyController =
-                    displayManagerInternal.getDisplayWindowPolicyController(mDisplayId);
+            mDwpcHelper = new DisplayWindowPolicyControllerHelper(this);
         }
 
         updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
@@ -2781,7 +2778,10 @@
         if (displayMetricsChanged || physicalDisplayChanged) {
             if (physicalDisplayChanged) {
                 // Reapply the window settings as the underlying physical display has changed.
-                mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+                // Do not include rotation settings here, postpone them until the display
+                // metrics are updated as rotation settings might depend on them
+                mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this,
+                        /* includeRotationSettings */ false);
             }
 
             // If there is an override set for base values - use it, otherwise use new values.
@@ -3450,10 +3450,7 @@
         mInputMonitor.dump(pw, "  ");
         pw.println();
         mInsetsStateController.dump(prefix, pw);
-        if (mDisplayWindowPolicyController != null) {
-            pw.println();
-            mDisplayWindowPolicyController.dump(prefix, pw);
-        }
+        mDwpcHelper.dump(prefix, pw);
     }
 
     @Override
@@ -3681,6 +3678,11 @@
         return true;
     }
 
+    /** Update the top activity and the uids of non-finishing activity */
+    void onRunningActivityChanged() {
+        mDwpcHelper.onRunningActivityChanged();
+    }
+
     /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */
     void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) {
         // Only record the TaskDisplayArea that handles orientation request.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f0e8b8f..f71fd1b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.Display.TYPE_INTERNAL;
 import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
@@ -1122,6 +1121,7 @@
                             if (!mNavButtonForcedVisible) {
                                 inOutFrame.inset(windowState.getLayoutingAttrs(
                                         displayFrames.mRotation).providedInternalInsets);
+                                inOutFrame.inset(win.mGivenContentInsets);
                             }
                         },
 
@@ -1190,9 +1190,12 @@
                                 break;
                         }
                         mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
-                                windowState, inOutFrame) -> inOutFrame.inset(
-                                windowState.getLayoutingAttrs(displayFrames.mRotation)
-                                        .providedInternalInsets), imeFrameProvider);
+                                windowState, inOutFrame) -> {
+                            inOutFrame.inset(
+                                    windowState.getLayoutingAttrs(displayFrames.mRotation)
+                                            .providedInternalInsets);
+                            inOutFrame.inset(win.mGivenContentInsets);
+                        }, imeFrameProvider);
                         mInsetsSourceWindowsExceptIme.add(win);
                     }
                 }
@@ -1715,8 +1718,7 @@
                 // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
                 // requests to hide the status bar.  Not sure if there is another way that to be the
                 // case though.
-                if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
-                        .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+                if (!topIsFullscreen) {
                     topAppHidesStatusBar = false;
                 }
             }
@@ -2423,8 +2425,7 @@
     private int updateSystemBarsLw(WindowState win, int disableFlags) {
         final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
         final boolean multiWindowTaskVisible =
-                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
-                        || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
+                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
         final boolean freeformRootTaskVisible =
                 defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
 
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
new file mode 100644
index 0000000..60d2a5d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.window.DisplayWindowPolicyController;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+class DisplayWindowPolicyControllerHelper {
+
+    private final DisplayContent mDisplayContent;
+
+    /**
+     * The policy controller of the windows that can be displayed on the virtual display.
+     *
+     * @see DisplayWindowPolicyController
+     */
+    @Nullable
+    private DisplayWindowPolicyController mDisplayWindowPolicyController;
+
+    /**
+     * The top non-finishing activity of this display.
+     */
+    private ActivityRecord mTopRunningActivity = null;
+
+    /**
+     * All the uids of non-finishing activity on this display.
+     * @see DisplayWindowPolicyController#onRunningAppsChanged(ArraySet)
+     */
+    private ArraySet<Integer> mRunningUid = new ArraySet<>();
+
+    DisplayWindowPolicyControllerHelper(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
+        mDisplayWindowPolicyController = mDisplayContent.mWmService.mDisplayManagerInternal
+                .getDisplayWindowPolicyController(mDisplayContent.mDisplayId);
+    }
+
+    /**
+     * Return {@code true} if there is DisplayWindowPolicyController.
+     */
+    public boolean hasController() {
+        return mDisplayWindowPolicyController != null;
+    }
+
+    /**
+     * @see DisplayWindowPolicyController#canContainActivities(List)
+     */
+    public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+        if (mDisplayWindowPolicyController == null) {
+            return true;
+        }
+        return mDisplayWindowPolicyController.canContainActivities(activities);
+    }
+
+    /**
+     * @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int)
+     */
+    boolean keepActivityOnWindowFlagsChanged(ActivityInfo aInfo, int flagChanges,
+            int privateFlagChanges) {
+        if (mDisplayWindowPolicyController == null) {
+            return true;
+        }
+
+        if (!mDisplayWindowPolicyController.isInterestedWindowFlags(
+                flagChanges, privateFlagChanges)) {
+            return true;
+        }
+
+        return mDisplayWindowPolicyController.keepActivityOnWindowFlagsChanged(
+                aInfo, flagChanges, privateFlagChanges);
+    }
+
+    /** Update the top activity and the uids of non-finishing activity */
+    void onRunningActivityChanged() {
+        if (mDisplayWindowPolicyController == null) {
+            return;
+        }
+
+        // Update top activity.
+        ActivityRecord topActivity = mDisplayContent.getTopActivity(false /* includeFinishing */,
+                true /* includeOverlays */);
+        if (topActivity != mTopRunningActivity) {
+            mTopRunningActivity = topActivity;
+            mDisplayWindowPolicyController.onTopActivityChanged(
+                    topActivity == null ? null : topActivity.info.getComponentName(),
+                    topActivity == null
+                            ? UserHandle.USER_NULL : topActivity.info.applicationInfo.uid);
+        }
+
+        // Update running uid.
+        final boolean[] notifyChanged = {false};
+        ArraySet<Integer> runningUids = new ArraySet<>();
+        mDisplayContent.forAllActivities((r) -> {
+            if (!r.finishing) {
+                notifyChanged[0] |= runningUids.add(r.getUid());
+            }
+        });
+
+        // We need to compare the size because if it is the following case, we can't know the
+        // existence of 3 in the forAllActivities() loop.
+        // Old set: 1,2,3
+        // New set: 1,2
+        if (notifyChanged[0] || (mRunningUid.size() != runningUids.size())) {
+            mRunningUid = runningUids;
+            mDisplayWindowPolicyController.onRunningAppsChanged(runningUids);
+        }
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        if (mDisplayWindowPolicyController != null) {
+            pw.println();
+            mDisplayWindowPolicyController.dump(prefix, pw);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 8260fd6..483c799 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -244,6 +244,10 @@
     }
 
     void applySettingsToDisplayLocked(DisplayContent dc) {
+        applySettingsToDisplayLocked(dc, /* includeRotationSettings */ true);
+    }
+
+    void applySettingsToDisplayLocked(DisplayContent dc, boolean includeRotationSettings) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
         final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
 
@@ -282,6 +286,8 @@
         boolean dontMoveToTop = settings.mDontMoveToTop != null
                 ? settings.mDontMoveToTop : false;
         dc.mDontMoveToTop = dontMoveToTop;
+
+        if (includeRotationSettings) applyRotationSettingsToDisplayLocked(dc);
     }
 
     void applyRotationSettingsToDisplayLocked(DisplayContent dc) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4225f21..44818a8 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -399,10 +399,11 @@
         if (recentsAnimationInputConsumer != null && focus != null) {
             final RecentsAnimationController recentsAnimationController =
                     mService.getRecentsAnimationController();
+            // Apply recents input consumer when the focusing window is in recents animation.
             final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null
                     && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord))
                     // Shell transitions doesn't use RecentsAnimationController
-                    || getWeak(mActiveRecentsActivity) != null;
+                    || getWeak(mActiveRecentsActivity) != null && focus.inTransition();
             if (shouldApplyRecentsInputConsumer) {
                 requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
                         recentsAnimationInputConsumer.mName);
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/OverlayHost.java
new file mode 100644
index 0000000..14f8983
--- /dev/null
+++ b/services/core/java/com/android/server/wm/OverlayHost.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.content.res.Configuration;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to assist WindowContainer in the hosting of
+ * SurfacePackage based overlays. Manages overlays inside
+ * one parent control, and manages the lifetime of that parent control
+ * in order to obscure details from WindowContainer.
+ *
+ * Also handles multiplexing of event dispatch and tracking of overlays
+ * to make things easier for WindowContainer.
+ */
+class OverlayHost {
+    // Lazily initialized when required
+    SurfaceControl mSurfaceControl;
+    final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>();
+    final WindowManagerService mWmService;
+
+    OverlayHost(WindowManagerService wms) {
+        mWmService = wms;
+    }
+
+    void requireOverlaySurfaceControl() {
+        if (mSurfaceControl == null) {
+            final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
+                .setContainerLayer()
+                .setHidden(true)
+                .setName("Overlay Host Leash");
+
+            mSurfaceControl = b.build();
+        }
+    }
+
+    void setParent(SurfaceControl.Transaction t, SurfaceControl newParent) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+        t.reparent(mSurfaceControl, newParent);
+        if (newParent != null) {
+            t.show(mSurfaceControl);
+        } else {
+            t.hide(mSurfaceControl);
+        }
+    }
+
+    void setLayer(SurfaceControl.Transaction t, int layer) {
+        if (mSurfaceControl != null) {
+            t.setLayer(mSurfaceControl, layer);
+        }
+    }
+
+    void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
+        requireOverlaySurfaceControl();
+        mOverlays.add(p);
+
+        SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+        t.reparent(p.getSurfaceControl(), mSurfaceControl)
+            .show(p.getSurfaceControl());
+        setParent(t,currentParent);
+        t.apply();
+    }
+
+    boolean removeOverlay(SurfaceControlViewHost.SurfacePackage p) {
+        final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+           if (l.getSurfaceControl().isSameSurface(p.getSurfaceControl())) {
+               mOverlays.remove(i);
+               t.reparent(l.getSurfaceControl(), null);
+               l.release();
+           }
+        }
+        t.apply();
+        return mOverlays.size() > 0;
+    }
+
+    void dispatchConfigurationChanged(Configuration c) {
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+           try {
+               l.getRemoteInterface().onConfigurationChanged(c);
+           } catch (Exception e) {
+               removeOverlay(l);
+           }
+        }
+    }
+
+    private void dispatchDetachedFromWindow() {
+        for (int i = mOverlays.size() - 1; i >= 0; i--) {
+            SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+            try {
+                l.getRemoteInterface().onDispatchDetachedFromWindow();
+            } catch (Exception e) {
+                // Oh well we are tearing down anyway.
+            }
+            l.release();
+        }
+    }
+
+    void release() {
+        dispatchDetachedFromWindow();
+        mOverlays.clear();
+        final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+        t.remove(mSurfaceControl).apply();
+        mSurfaceControl = null;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index dca0bbd..a049d65 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -27,7 +27,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
@@ -1370,16 +1369,6 @@
         switch (task.getWindowingMode()) {
             case WINDOWING_MODE_PINNED:
                 return false;
-            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (DEBUG_RECENTS_TRIM_TASKS) {
-                    Slog.d(TAG, "\ttop=" + task.getRootTask().getTopMostTask());
-                }
-                final Task rootTask = task.getRootTask();
-                if (rootTask != null && rootTask.getTopMostTask() == task) {
-                    // Only the non-top task of the primary split screen mode is visible
-                    return false;
-                }
-                break;
             case WINDOWING_MODE_MULTI_WINDOW:
                 // Ignore tasks that are always on top
                 if (task.isAlwaysOnTopWhenVisible()) {
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index f9d7b53..6ed59e9 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -25,14 +25,15 @@
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.IWindow;
 import android.view.SurfaceControl;
+import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.animation.Animation;
 
@@ -135,12 +136,47 @@
                 ANIMATION_TYPE_WINDOW_ANIMATION);
     }
 
-    @Nullable
-    IBinder getAccessibilityWindowToken() {
-        if (mAccessibilityWindow != null) {
-            return mAccessibilityWindow.asBinder();
+    WindowInfo getWindowInfo() {
+        if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER
+                && mShellRootLayer != SHELL_ROOT_LAYER_PIP) {
+            return null;
         }
-        return null;
+        if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER
+                && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
+            return null;
+        }
+        if (mShellRootLayer == SHELL_ROOT_LAYER_PIP
+                && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) {
+            return null;
+        }
+        if (mAccessibilityWindow == null) {
+            return null;
+        }
+        WindowInfo windowInfo = WindowInfo.obtain();
+        windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId;
+        windowInfo.type = mToken.windowType;
+        windowInfo.layer = mToken.getWindowLayerFromType();
+        windowInfo.token = mAccessibilityWindow.asBinder();
+        windowInfo.focused = false;
+        windowInfo.hasFlagWatchOutsideTouch = false;
+        final Rect regionRect = new Rect();
+
+
+        // DividerView
+        if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) {
+            windowInfo.inPictureInPicture = false;
+            mDisplayContent.getDockedDividerController().getTouchRegion(regionRect);
+            windowInfo.regionInScreen.set(regionRect);
+            windowInfo.title = "Splitscreen Divider";
+        }
+        // PipMenuView
+        if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) {
+            windowInfo.inPictureInPicture = true;
+            mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect);
+            windowInfo.regionInScreen.set(regionRect);
+            windowInfo.title = "Picture-in-Picture menu";
+        }
+        return windowInfo;
     }
 
     void setAccessibilityWindow(IWindow window) {
@@ -161,5 +197,9 @@
                 mAccessibilityWindow = null;
             }
         }
+        if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
+            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
+                    mDisplayContent.getDisplayId());
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 43038ce..6d5957e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -30,7 +30,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
@@ -125,6 +124,8 @@
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
 import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
@@ -609,6 +610,8 @@
      */
     ActivityRecord mChildPipActivity;
 
+    boolean mLastSurfaceShowing = true;
+
     private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
             Intent _affinityIntent, String _affinity, String _rootAffinity,
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -925,13 +928,6 @@
             if (!animate) {
                 mTaskSupervisor.mNoAnimActivities.add(topActivity);
             }
-
-            if (toRootTaskWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    && moveRootTaskMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT) {
-                // Move recents to front so it is not behind root home task when going into docked
-                // mode
-                mTaskSupervisor.moveRecentsRootTaskToFront(reason);
-            }
         } finally {
             mAtmService.continueWindowLayout();
         }
@@ -1749,7 +1745,7 @@
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
         return mTaskSupervisor.canPlaceEntityOnDisplay(displayId,
-                -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
+                -1 /* don't check PID */, -1 /* don't check UID */, this);
     }
 
     /**
@@ -2326,8 +2322,7 @@
 
         final int windowingMode = getWindowingMode();
         if (!isActivityTypeStandardOrUndefined()
-                || windowingMode == WINDOWING_MODE_FULLSCREEN
-                || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
+                || windowingMode == WINDOWING_MODE_FULLSCREEN) {
             return isResizeable() ? rootTask.getRequestedOverrideBounds() : null;
         } else if (!getWindowConfiguration().persistTaskBounds()) {
             return rootTask.getRequestedOverrideBounds();
@@ -3308,6 +3303,17 @@
         if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
             scheduleAnimation();
         }
+
+        // We intend to let organizer manage task visibility but it doesn't
+        // have enough information until we finish shell transitions.
+        // In the mean time we do an easy fix here.
+        final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS);
+        if (mSurfaceControl != null) {
+            if (show != mLastSurfaceShowing) {
+                getSyncTransaction().setVisibility(mSurfaceControl, show);
+            }
+        }
+        mLastSurfaceShowing = show;
     }
 
     @Override
@@ -5971,7 +5977,19 @@
     }
 
     void reparent(TaskDisplayArea newParent, boolean onTop) {
-        reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        if (newParent == null) {
+            throw new IllegalArgumentException("Task can't reparent to null " + this);
+        }
+
+        if (getParent() == newParent) {
+            throw new IllegalArgumentException("Task=" + this + " already child of " + newParent);
+        }
+
+        if (canBeLaunchedOnDisplay(newParent.getDisplayId())) {
+            reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        } else {
+            Slog.w(TAG, "Task=" + this + " can't reparent to " + newParent);
+        }
     }
 
     void setLastRecentsAnimationTransaction(@NonNull PictureInPictureSurfaceTransaction transaction,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d133ca9..b681a96 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -398,8 +398,16 @@
             Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
                     + mResumedActivity + " to:" + r + " reason:" + reason);
         }
+        final ActivityRecord prevR = mResumedActivity;
         mResumedActivity = r;
         mTaskSupervisor.updateTopResumedActivityIfNeeded();
+        if (r == null && prevR.mDisplayContent != null
+                && prevR.mDisplayContent.getFocusedRootTask() == null) {
+            // Only need to notify DWPC when no activity will resume.
+            prevR.mDisplayContent.onRunningActivityChanged();
+        } else if (r != null) {
+            r.mDisplayContent.onRunningActivityChanged();
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index c7fdefc..123ca88 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -22,6 +22,7 @@
 import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -497,6 +498,23 @@
         return null;
     }
 
+    private boolean shouldSendEventWhenTaskInvisible(@NonNull Task task,
+            @NonNull PendingTaskFragmentEvent event) {
+        final TaskFragmentOrganizerState state =
+                mTaskFragmentOrganizerState.get(event.mTaskFragmentOrg.asBinder());
+        final TaskFragmentInfo lastInfo = state.mLastSentTaskFragmentInfos.get(event.mTaskFragment);
+        final TaskFragmentInfo info = event.mTaskFragment.getTaskFragmentInfo();
+        // Send an info changed callback if this event is for the last activities to finish in a
+        // Task so that the {@link TaskFragmentOrganizer} can delete this TaskFragment. Otherwise,
+        // the Task may be removed before it becomes visible again to send this event because it no
+        // longer has activities. As a result, the organizer will never get this info changed event
+        // and will not delete the TaskFragment because the organizer thinks the TaskFragment still
+        // has running activities.
+        return event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED
+                && task.topRunningActivity() == null && lastInfo != null
+                && lastInfo.getRunningActivityCount() > 0 && info.getRunningActivityCount() == 0;
+    }
+
     void dispatchPendingEvents() {
         if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
                 || mPendingTaskFragmentEvents.isEmpty()) {
@@ -510,7 +528,8 @@
             final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
             final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
             if (task != null && (task.lastActiveTime <= event.mDeferTime
-                    || !isTaskVisible(task, visibleTasks, invisibleTasks))) {
+                    || !(isTaskVisible(task, visibleTasks, invisibleTasks)
+                    || shouldSendEventWhenTaskInvisible(task, event)))) {
                 // Defer sending events to the TaskFragment until the host task is active again.
                 event.mDeferTime = task.lastActiveTime;
                 continue;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a477108..b13c9a9 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -79,6 +79,7 @@
 import android.window.TransitionInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -1271,6 +1272,14 @@
                 change.setAllowEnterPip(topMostActivity != null
                         && topMostActivity.checkEnterPictureInPictureAppOpsState());
             }
+            final ActivityRecord activityRecord = target.asActivityRecord();
+            if (activityRecord != null) {
+                final Task arTask = activityRecord.getTask();
+                final int backgroundColor = ColorUtils.setAlphaComponent(
+                        arTask.getTaskDescription().getBackgroundColor(), 255);
+                change.setBackgroundColor(backgroundColor);
+            }
+
             out.addChange(change);
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 61acb97..4006848 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -85,6 +85,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
 import android.view.TaskTransitionSpec;
@@ -312,6 +313,8 @@
 
     private final List<WindowContainerListener> mListeners = new ArrayList<>();
 
+    private OverlayHost mOverlayHost;
+
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
         mTransitionController = mWmService.mAtmService.getTransitionController();
@@ -341,6 +344,9 @@
         super.onConfigurationChanged(newParentConfig);
         updateSurfacePositionNonOrganized();
         scheduleAnimation();
+        if (mOverlayHost != null) {
+            mOverlayHost.dispatchConfigurationChanged(getConfiguration());
+        }
     }
 
     void reparent(WindowContainer newParent, int position) {
@@ -387,6 +393,8 @@
 
         if (mParent != null) {
             mParent.onChildAdded(this);
+        } else if (mSurfaceAnimator.hasLeash()) {
+            mSurfaceAnimator.cancelAnimation();
         }
         if (!mReparenting) {
             onSyncReparent(oldParent, mParent);
@@ -487,6 +495,11 @@
                 t.reparent(sc, mSurfaceControl);
             }
         }
+
+        if (mOverlayHost != null) {
+            mOverlayHost.setParent(t, mSurfaceControl);
+        }
+
         scheduleAnimation();
     }
 
@@ -632,6 +645,10 @@
             mLastSurfacePosition.set(0, 0);
             scheduleAnimation();
         }
+        if (mOverlayHost != null) {
+            mOverlayHost.release();
+            mOverlayHost = null;
+        }
 
         // This must happen after updating the surface so that sync transactions can be handled
         // properly.
@@ -2308,6 +2325,9 @@
                 wc.assignLayer(t, layer++);
             }
         }
+        if (mOverlayHost != null) {
+            mOverlayHost.setLayer(t, layer++);
+        }
     }
 
     void assignChildLayers() {
@@ -3570,4 +3590,18 @@
         void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                 @AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
     }
+
+    void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+        if (mOverlayHost == null) {
+            mOverlayHost = new OverlayHost(mWmService);
+        }
+        mOverlayHost.addOverlay(overlay, mSurfaceControl);
+    }
+
+    void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+        if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) {
+            mOverlayHost.release();
+            mOverlayHost = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 62c674b..1ab191b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -32,6 +32,7 @@
 import android.view.InputChannel;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControlViewHost;
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 
@@ -767,4 +768,16 @@
      *         {@code false} otherwise.
      */
     public abstract boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken);
+
+    /**
+     * Internal methods for other parts of SystemServer to manage
+     * SurfacePackage based overlays on tasks.
+     *
+     * Callers prepare a view hierarchy with SurfaceControlViewHost
+     * and send the package to WM here. The remote view hierarchy will receive
+     * configuration change, lifecycle events, etc, forwarded over the
+     * ISurfaceControlViewHost interface inside the SurfacePackage.
+     */
+    public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
+    public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c239e68..e4216bf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -267,6 +267,7 @@
 import android.view.ScrollCaptureResponse;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceSession;
 import android.view.TaskTransitionSpec;
 import android.view.View;
@@ -2245,6 +2246,15 @@
                     winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
                             & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
                 }
+                if (win.mActivityRecord != null
+                        && !displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
+                                win.mActivityRecord.info, flagChanges, privateFlagChanges)) {
+                    mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
+                            win.mActivityRecord.getTask()));
+                    Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
+                            + " can't remain on display " + displayContent.getDisplayId());
+                    return 0;
+                }
             }
 
             if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
@@ -5064,6 +5074,7 @@
         public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
         public static final int LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED = 63;
         public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
+        public static final int REPARENT_TASK_TO_DEFAULT_DISPLAY = 65;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -5381,6 +5392,15 @@
                     }
                     break;
                 }
+                case REPARENT_TASK_TO_DEFAULT_DISPLAY: {
+                    synchronized (mGlobalLock) {
+                        Task task = (Task) msg.obj;
+                        task.reparent(mRoot.getDefaultTaskDisplayArea(), true /* onTop */);
+                        // Resume focusable root task after reparenting to another display area.
+                        task.resumeNextFocusAfterReparent();
+                    }
+                    break;
+                }
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
@@ -7891,6 +7911,28 @@
         public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
             return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
         }
+
+        @Override
+        public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+            synchronized (mGlobalLock) {
+                final Task task = mRoot.getRootTask(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("no task with taskId" + taskId);
+                }
+                task.addOverlay(overlay);
+            }
+        }
+
+        @Override
+        public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+            synchronized (mGlobalLock) {
+                final Task task = mRoot.getRootTask(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("no task with taskId" + taskId);
+                }
+                task.removeOverlay(overlay);
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5bbe2cd..94d4a77 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4809,6 +4809,9 @@
         if (isAnimating()) {
             return;
         }
+        if (mWmService.mAccessibilityController.hasCallbacks()) {
+            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
+        }
 
         if (!isSelfOrAncestorWindowAnimatingExit()) {
             return;
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 34ae469..5a7cee9 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -27,12 +27,16 @@
 using hardware::gnss::GnssData;
 using hardware::gnss::GnssMeasurement;
 using hardware::gnss::SatellitePvt;
+using GnssAgc = hardware::gnss::GnssData::GnssAgc;
 
 namespace {
 jclass class_arrayList;
 jclass class_clockInfo;
 jclass class_correlationVectorBuilder;
+jclass class_gnssAgc;
+jclass class_gnssAgcBuilder;
 jclass class_gnssMeasurementsEvent;
+jclass class_gnssMeasurementsEventBuilder;
 jclass class_gnssMeasurement;
 jclass class_gnssClock;
 jclass class_positionEcef;
@@ -47,7 +51,16 @@
 jmethodID method_correlationVectorBuilderSetMagnitude;
 jmethodID method_correlationVectorBuilderSetSamplingStartMeters;
 jmethodID method_correlationVectorBuilderSetSamplingWidthMeters;
-jmethodID method_gnssMeasurementsEventCtor;
+jmethodID method_gnssAgcBuilderCtor;
+jmethodID method_gnssAgcBuilderSetLevelDb;
+jmethodID method_gnssAgcBuilderSetConstellationType;
+jmethodID method_gnssAgcBuilderSetCarrierFrequencyHz;
+jmethodID method_gnssAgcBuilderBuild;
+jmethodID method_gnssMeasurementsEventBuilderCtor;
+jmethodID method_gnssMeasurementsEventBuilderSetClock;
+jmethodID method_gnssMeasurementsEventBuilderSetMeasurements;
+jmethodID method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls;
+jmethodID method_gnssMeasurementsEventBuilderBuild;
 jmethodID method_gnssMeasurementsSetCorrelationVectors;
 jmethodID method_gnssMeasurementsSetSatellitePvt;
 jmethodID method_gnssClockCtor;
@@ -69,12 +82,55 @@
 void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) {
     method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData",
                                                     "(Landroid/location/GnssMeasurementsEvent;)V");
+
+    // Initialize GnssMeasurement related classes and methods
     jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent");
     class_gnssMeasurementsEvent = (jclass)env->NewGlobalRef(gnssMeasurementsEventClass);
-    method_gnssMeasurementsEventCtor =
-            env->GetMethodID(class_gnssMeasurementsEvent, "<init>",
-                             "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V");
+    jclass gnssMeasurementsEventBuilderClass =
+            env->FindClass("android/location/GnssMeasurementsEvent$Builder");
+    class_gnssMeasurementsEventBuilder =
+            (jclass)env->NewGlobalRef(gnssMeasurementsEventBuilderClass);
+    method_gnssMeasurementsEventBuilderCtor =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "<init>", "()V");
+    method_gnssMeasurementsEventBuilderSetClock =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "setClock",
+                             "(Landroid/location/GnssClock;)"
+                             "Landroid/location/GnssMeasurementsEvent$Builder;");
+    method_gnssMeasurementsEventBuilderSetMeasurements =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "setMeasurements",
+                             "([Landroid/location/GnssMeasurement;)"
+                             "Landroid/location/GnssMeasurementsEvent$Builder;");
+    method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "setGnssAutomaticGainControls",
+                             "([Landroid/location/GnssAutomaticGainControl;)"
+                             "Landroid/location/GnssMeasurementsEvent$Builder;");
+    method_gnssMeasurementsEventBuilderBuild =
+            env->GetMethodID(class_gnssMeasurementsEventBuilder, "build",
+                             "()Landroid/location/GnssMeasurementsEvent;");
 
+    // Initialize GnssAgc related classes and methods
+    jclass gnssAgcClass = env->FindClass("android/location/GnssAutomaticGainControl");
+    class_gnssAgc = (jclass)env->NewGlobalRef(gnssAgcClass);
+    jclass gnssAgcBuilderClass =
+            env->FindClass("android/location/GnssAutomaticGainControl$Builder");
+    class_gnssAgcBuilder = (jclass)env->NewGlobalRef(gnssAgcBuilderClass);
+    method_gnssAgcBuilderCtor = env->GetMethodID(class_gnssAgcBuilder, "<init>", "()V");
+    method_gnssAgcBuilderSetLevelDb =
+            env->GetMethodID(class_gnssAgcBuilder, "setLevelDb",
+                             "(D)"
+                             "Landroid/location/GnssAutomaticGainControl$Builder;");
+    method_gnssAgcBuilderSetConstellationType =
+            env->GetMethodID(class_gnssAgcBuilder, "setConstellationType",
+                             "(I)"
+                             "Landroid/location/GnssAutomaticGainControl$Builder;");
+    method_gnssAgcBuilderSetCarrierFrequencyHz =
+            env->GetMethodID(class_gnssAgcBuilder, "setCarrierFrequencyHz",
+                             "(J)"
+                             "Landroid/location/GnssAutomaticGainControl$Builder;");
+    method_gnssAgcBuilderBuild = env->GetMethodID(class_gnssAgcBuilder, "build",
+                                                  "()Landroid/location/GnssAutomaticGainControl;");
+
+    // Initialize GnssMeasurement related classes and methods
     jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement");
     class_gnssMeasurement = (jclass)env->NewGlobalRef(gnssMeasurementClass);
     method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
@@ -152,14 +208,25 @@
 }
 
 void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
-                        jobjectArray measurementArray) {
-    jobject gnssMeasurementsEvent =
-            env->NewObject(class_gnssMeasurementsEvent, method_gnssMeasurementsEventCtor, clock,
-                           measurementArray);
+                        jobjectArray measurementArray, jobjectArray gnssAgcArray) {
+    jobject gnssMeasurementsEventBuilderObject =
+            env->NewObject(class_gnssMeasurementsEventBuilder,
+                           method_gnssMeasurementsEventBuilderCtor);
+    env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                          method_gnssMeasurementsEventBuilderSetClock, clock);
+    env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                          method_gnssMeasurementsEventBuilderSetMeasurements, measurementArray);
+    env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                          method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls,
+                          gnssAgcArray);
+    jobject gnssMeasurementsEventObject =
+            env->CallObjectMethod(gnssMeasurementsEventBuilderObject,
+                                  method_gnssMeasurementsEventBuilderBuild);
 
-    env->CallVoidMethod(callbacksObj, method_reportMeasurementData, gnssMeasurementsEvent);
+    env->CallVoidMethod(callbacksObj, method_reportMeasurementData, gnssMeasurementsEventObject);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    env->DeleteLocalRef(gnssMeasurementsEvent);
+    env->DeleteLocalRef(gnssMeasurementsEventBuilderObject);
+    env->DeleteLocalRef(gnssMeasurementsEventObject);
 }
 
 template <class T_Measurement, class T_Flags>
@@ -289,9 +356,13 @@
     JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor);
     translateGnssClock(env, data, gnssClockJavaObject);
     jobject clock = gnssClockJavaObject.get();
-
     jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements);
-    setMeasurementData(env, mCallbacksObj, clock, measurementArray);
+
+    jobjectArray gnssAgcArray = nullptr;
+    if (data.gnssAgcs.has_value()) {
+        gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs.value());
+    }
+    setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray);
 
     env->DeleteLocalRef(clock);
     env->DeleteLocalRef(measurementArray);
@@ -436,6 +507,38 @@
     return gnssMeasurementArray;
 }
 
+jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(
+        JNIEnv* env, const std::vector<std::optional<GnssAgc>>& agcs) {
+    if (agcs.size() == 0) {
+        return nullptr;
+    }
+
+    jobjectArray gnssAgcArray =
+            env->NewObjectArray(agcs.size(), class_gnssAgc, nullptr /* initialElement */);
+
+    for (uint16_t i = 0; i < agcs.size(); ++i) {
+        if (!agcs[i].has_value()) {
+            continue;
+        }
+        const GnssAgc& gnssAgc = agcs[i].value();
+
+        jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor);
+        env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb,
+                              gnssAgc.agcLevelDb);
+        env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetConstellationType,
+                              (int)gnssAgc.constellation);
+        env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetCarrierFrequencyHz,
+                              gnssAgc.carrierFrequencyHz);
+        jobject agcObject = env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderBuild);
+
+        env->SetObjectArrayElement(gnssAgcArray, i, agcObject);
+        env->DeleteLocalRef(agcBuilderObject);
+        env->DeleteLocalRef(agcObject);
+    }
+
+    return gnssAgcArray;
+}
+
 void GnssMeasurementCallbackAidl::translateGnssClock(JNIEnv* env, const GnssData& data,
                                                      JavaObject& object) {
     setElapsedRealtimeFields<ElapsedRealtime, ElapsedRealtime>(data.elapsedRealtime, object);
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 32200fd..9b346312 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -48,7 +48,7 @@
 void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz);
 
 void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock,
-                        jobjectArray measurementArray);
+                        jobjectArray measurementArray, jobjectArray gnssAgcArray);
 
 class GnssMeasurementCallbackAidl : public hardware::gnss::BnGnssMeasurementCallback {
 public:
@@ -62,6 +62,8 @@
 
     jobjectArray translateAllGnssMeasurements(
             JNIEnv* env, const std::vector<hardware::gnss::GnssMeasurement>& measurements);
+    jobjectArray translateAllGnssAgcs(
+            JNIEnv* env, const std::vector<std::optional<hardware::gnss::GnssData::GnssAgc>>& agcs);
 
     void translateAndSetGnssData(const hardware::gnss::GnssData& data);
 
@@ -139,7 +141,7 @@
     size_t count = getMeasurementCount(data);
     jobjectArray measurementArray =
             translateAllGnssMeasurements(env, data.measurements.data(), count);
-    setMeasurementData(env, mCallbacksObj, clock, measurementArray);
+    setMeasurementData(env, mCallbacksObj, clock, measurementArray, nullptr);
 
     env->DeleteLocalRef(clock);
     env->DeleteLocalRef(measurementArray);
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 2f4dd57..baf2ede 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -58,6 +58,15 @@
                 <xs:element type="sensorDetails" name="proxSensor">
                     <xs:annotation name="final"/>
                 </xs:element>
+
+                <!-- Length of the ambient light horizon used to calculate the long & short term
+                estimates of ambient light in milliseconds.-->
+                <xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonLong">
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <xs:element type="xs:nonNegativeInteger" name="ambientLightHorizonShort">
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 5b2b87c..6f97431 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -18,6 +18,8 @@
 
   public class DisplayConfiguration {
     ctor public DisplayConfiguration();
+    method public final java.math.BigInteger getAmbientLightHorizonLong();
+    method public final java.math.BigInteger getAmbientLightHorizonShort();
     method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
     method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
     method public final com.android.server.display.config.SensorDetails getLightSensor();
@@ -29,6 +31,8 @@
     method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
     method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+    method public final void setAmbientLightHorizonLong(java.math.BigInteger);
+    method public final void setAmbientLightHorizonShort(java.math.BigInteger);
     method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 55ab8c3..ebe9f93 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,16 +17,20 @@
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyDrawableResource;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.ManagedProfileProvisioningParams;
+import android.app.admin.ParcelableResource;
 import android.content.ComponentName;
 import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.server.SystemService;
 
+import java.util.List;
+
 /**
  * Defines the required interface for IDevicePolicyManager implemenation.
  *
@@ -161,4 +165,16 @@
     public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) {
         return false;
     }
+
+    @Override
+    public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){}
+
+    @Override
+    public void resetDrawables(@NonNull int[] drawableIds){}
+
+    @Override
+    public ParcelableResource getDrawable(
+            int drawableId, int drawableStyle, int drawableSource) {
+        return null;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
new file mode 100644
index 0000000..53422940
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2022 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.devicepolicy;
+
+import static android.app.admin.DevicePolicyResources.Drawable.Source.UPDATABLE_DRAWABLE_SOURCES;
+import static android.app.admin.DevicePolicyResources.Drawable.Style;
+import static android.app.admin.DevicePolicyResources.Drawable.Style.UPDATABLE_DRAWABLE_STYLES;
+import static android.app.admin.DevicePolicyResources.Drawable.UPDATABLE_DRAWABLE_IDS;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyDrawableResource;
+import android.app.admin.DevicePolicyResources;
+import android.app.admin.ParcelableResource;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A helper class for {@link DevicePolicyManagerService} to store/retrieve updated device
+ * management resources.
+ */
+class DeviceManagementResourcesProvider {
+    private static final String TAG = "DevicePolicyManagerService";
+
+    private static final String UPDATED_RESOURCES_XML = "updated_resources.xml";
+    private static final String TAG_ROOT = "root";
+    private static final String TAG_DRAWABLE_STYLE_ENTRY = "drawable-style-entry";
+    private static final String TAG_DRAWABLE_SOURCE_ENTRY = "drawable-source-entry";
+    private static final String ATTR_DRAWABLE_STYLE_SIZE = "drawable-style-size";
+    private static final String ATTR_DRAWABLE_SOURCE_SIZE = "drawable-source-size";
+    private static final String ATTR_DRAWABLE_STYLE = "drawable-style";
+    private static final String ATTR_DRAWABLE_SOURCE = "drawable-source";
+    private static final String ATTR_DRAWABLE_ID = "drawable-id";
+
+
+    private final Map<Integer, Map<Integer, ParcelableResource>>
+            mUpdatedDrawablesForStyle = new HashMap<>();
+
+    private final Map<Integer, Map<Integer, ParcelableResource>>
+            mUpdatedDrawablesForSource = new HashMap<>();
+
+    private final Object mLock = new Object();
+    private final Injector mInjector;
+
+    DeviceManagementResourcesProvider() {
+        this(new Injector());
+    }
+
+    DeviceManagementResourcesProvider(Injector injector) {
+        mInjector = requireNonNull(injector);
+    }
+
+    /**
+     * Returns {@code false} if no resources were updated.
+     */
+    boolean updateDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
+        boolean updated = false;
+        for (int i = 0; i < drawables.size(); i++) {
+            int drawableId = drawables.get(i).getDrawableId();
+            int drawableStyle = drawables.get(i).getDrawableStyle();
+            int drawableSource = drawables.get(i).getDrawableSource();
+            ParcelableResource resource = drawables.get(i).getResource();
+
+            Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+
+            if (drawableSource == DevicePolicyResources.Drawable.Source.UNDEFINED) {
+                updated |= updateDrawable(drawableId, drawableStyle, resource);
+            } else {
+                updated |= updateDrawableForSource(drawableId, drawableSource, resource);
+            }
+        }
+        if (!updated) {
+            return false;
+        }
+        synchronized (mLock) {
+            write();
+            return true;
+        }
+    }
+
+    private boolean updateDrawable(
+            int drawableId, int drawableStyle, ParcelableResource updatableResource) {
+        if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
+            throw new IllegalArgumentException(
+                    "Can't update drawable resource, invalid drawable " + "id " + drawableId);
+        }
+        if (!UPDATABLE_DRAWABLE_STYLES.contains(drawableStyle)) {
+            throw new IllegalArgumentException(
+                    "Can't update drawable resource, invalid style id " + drawableStyle);
+        }
+        synchronized (mLock) {
+            if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
+                mUpdatedDrawablesForStyle.put(drawableId, new HashMap<>());
+            }
+            ParcelableResource current = mUpdatedDrawablesForStyle.get(drawableId).get(
+                    drawableStyle);
+            if (updatableResource.equals(current)) {
+                return false;
+            }
+            mUpdatedDrawablesForStyle.get(drawableId).put(drawableStyle, updatableResource);
+            return true;
+        }
+    }
+
+    // TODO(b/214576716): change this to respect style
+    private boolean updateDrawableForSource(
+            int drawableId, int drawableSource, ParcelableResource updatableResource) {
+        if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
+            throw new IllegalArgumentException("Can't update drawable resource, invalid drawable "
+                    + "id " + drawableId);
+        }
+        if (!UPDATABLE_DRAWABLE_SOURCES.contains(drawableSource)) {
+            throw new IllegalArgumentException("Can't update drawable resource, invalid source id "
+                    + drawableSource);
+        }
+        synchronized (mLock) {
+            if (!mUpdatedDrawablesForSource.containsKey(drawableId)) {
+                mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
+            }
+            ParcelableResource current = mUpdatedDrawablesForSource.get(drawableId).get(
+                    drawableSource);
+            if (updatableResource.equals(current)) {
+                return false;
+            }
+            mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, updatableResource);
+            return true;
+        }
+    }
+
+    /**
+     * Returns {@code false} if no resources were removed.
+     */
+    boolean removeDrawables(@NonNull int[] drawableIds) {
+        synchronized (mLock) {
+            boolean removed = false;
+            for (int i = 0; i < drawableIds.length; i++) {
+                int drawableId = drawableIds[i];
+                removed |= mUpdatedDrawablesForStyle.remove(drawableId) != null
+                        || mUpdatedDrawablesForSource.remove(drawableId) != null;
+            }
+            if (!removed) {
+                return false;
+            }
+            write();
+            return true;
+        }
+    }
+
+    @Nullable
+    ParcelableResource getDrawable(
+            int drawableId, int drawableStyle, int drawableSource) {
+        if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
+            Log.e(TAG, "Can't get updated drawable resource, invalid drawable id "
+                    + drawableId);
+            return null;
+        }
+        if (!UPDATABLE_DRAWABLE_STYLES.contains(drawableStyle)) {
+            Log.e(TAG, "Can't get updated drawable resource, invalid style id "
+                    + drawableStyle);
+            return null;
+        }
+        if (!UPDATABLE_DRAWABLE_SOURCES.contains(drawableSource)) {
+            Log.e(TAG, "Can't get updated drawable resource, invalid source id "
+                    + drawableSource);
+            return null;
+        }
+        if (mUpdatedDrawablesForSource.containsKey(drawableId)
+                && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
+            return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource);
+        }
+        if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
+            Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
+            return null;
+        }
+        if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) {
+            return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle);
+        }
+
+        if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(Style.DEFAULT)) {
+            return mUpdatedDrawablesForStyle.get(drawableId).get(Style.DEFAULT);
+        }
+        Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
+        return null;
+    }
+
+    private void write() {
+        Log.d(TAG, "Writing updated resources to file.");
+        new ResourcesReaderWriter().writeToFileLocked();
+    }
+
+    void load() {
+        synchronized (mLock) {
+            new ResourcesReaderWriter().readFromFileLocked();
+        }
+    }
+
+    private File getResourcesFile() {
+        return new File(mInjector.environmentGetDataSystemDirectory(), UPDATED_RESOURCES_XML);
+    }
+
+    private class ResourcesReaderWriter {
+        private final File mFile;
+        private ResourcesReaderWriter() {
+            mFile = getResourcesFile();
+        }
+
+        void writeToFileLocked() {
+            Log.d(TAG, "Writing to " + mFile);
+
+            AtomicFile f = new AtomicFile(mFile);
+            FileOutputStream outputStream = null;
+            try {
+                outputStream = f.startWrite();
+                TypedXmlSerializer out = Xml.resolveSerializer(outputStream);
+
+                // Root tag
+                out.startDocument(null, true);
+                out.startTag(null, TAG_ROOT);
+
+                // Actual content
+                writeInner(out);
+
+                // Close root
+                out.endTag(null, TAG_ROOT);
+                out.endDocument();
+                out.flush();
+
+                // Commit the content.
+                f.finishWrite(outputStream);
+                outputStream = null;
+
+            } catch (IOException e) {
+                Log.e(TAG, "Exception when writing", e);
+                if (outputStream != null) {
+                    f.failWrite(outputStream);
+                }
+            }
+        }
+
+        void readFromFileLocked() {
+            if (!mFile.exists()) {
+                Log.d(TAG, "" + mFile + " doesn't exist");
+                return;
+            }
+
+            Log.d(TAG, "Reading from " + mFile);
+            AtomicFile f = new AtomicFile(mFile);
+            InputStream input = null;
+            try {
+                input = f.openRead();
+                TypedXmlPullParser parser = Xml.resolvePullParser(input);
+
+                int type;
+                int depth = 0;
+                while ((type = parser.next()) != TypedXmlPullParser.END_DOCUMENT) {
+                    switch (type) {
+                        case TypedXmlPullParser.START_TAG:
+                            depth++;
+                            break;
+                        case TypedXmlPullParser.END_TAG:
+                            depth--;
+                            // fallthrough
+                        default:
+                            continue;
+                    }
+                    // Check the root tag
+                    String tag = parser.getName();
+                    if (depth == 1) {
+                        if (!TAG_ROOT.equals(tag)) {
+                            Log.e(TAG, "Invalid root tag: " + tag);
+                            return;
+                        }
+                        continue;
+                    }
+                    // readInner() will only see START_TAG at depth >= 2.
+                    if (!readInner(parser, depth, tag)) {
+                        return; // Error
+                    }
+                }
+            } catch (XmlPullParserException | IOException e) {
+                Log.e(TAG, "Error parsing resources file", e);
+            } finally {
+                IoUtils.closeQuietly(input);
+            }
+        }
+
+        void writeInner(TypedXmlSerializer out) throws IOException {
+            if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) {
+                for (Map.Entry<Integer, Map<Integer, ParcelableResource>> drawableEntry
+                        : mUpdatedDrawablesForStyle.entrySet()) {
+                    out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
+                    out.attributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
+                    out.attributeInt(
+                            /* namespace= */ null,
+                            ATTR_DRAWABLE_STYLE_SIZE,
+                            drawableEntry.getValue().size());
+                    int counter = 0;
+                    for (Map.Entry<Integer, ParcelableResource> styleEntry
+                            : drawableEntry.getValue().entrySet()) {
+                        out.attributeInt(
+                                /* namespace= */ null,
+                                ATTR_DRAWABLE_STYLE + (counter++),
+                                styleEntry.getKey());
+                        styleEntry.getValue().writeToXmlFile(out);
+                    }
+                    out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
+                }
+            }
+            if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) {
+                for (Map.Entry<Integer, Map<Integer, ParcelableResource>> drawableEntry
+                        : mUpdatedDrawablesForSource.entrySet()) {
+                    out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
+                    out.attributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
+                    out.attributeInt(
+                            /* namespace= */ null,
+                            ATTR_DRAWABLE_SOURCE_SIZE,
+                            drawableEntry.getValue().size());
+                    int counter = 0;
+                    for (Map.Entry<Integer, ParcelableResource> sourceEntry
+                            : drawableEntry.getValue().entrySet()) {
+                        out.attributeInt(
+                                /* namespace= */ null,
+                                ATTR_DRAWABLE_SOURCE + (counter++),
+                                sourceEntry.getKey());
+                        sourceEntry.getValue().writeToXmlFile(out);
+                    }
+                    out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
+                }
+            }
+        }
+
+        private boolean readInner(
+                TypedXmlPullParser parser, int depth, String tag)
+                throws XmlPullParserException, IOException {
+            if (depth > 2) {
+                return true; // Ignore
+            }
+            switch (tag) {
+                case TAG_DRAWABLE_STYLE_ENTRY:
+                    int drawableId = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID);
+                    mUpdatedDrawablesForStyle.put(
+                            drawableId,
+                            new HashMap<>());
+                    int size = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE);
+                    for (int i = 0; i < size; i++) {
+                        int style = parser.getAttributeInt(
+                                /* namespace= */ null, ATTR_DRAWABLE_STYLE + i);
+                        mUpdatedDrawablesForStyle.get(drawableId).put(
+                                style,
+                                ParcelableResource.createFromXml(parser));
+                    }
+                    break;
+                case TAG_DRAWABLE_SOURCE_ENTRY:
+                    drawableId = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_ID);
+                    mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
+                    size = parser.getAttributeInt(
+                            /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE);
+                    for (int i = 0; i < size; i++) {
+                        int source = parser.getAttributeInt(
+                                /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i);
+                        mUpdatedDrawablesForSource.get(drawableId).put(
+                                source,
+                                ParcelableResource.createFromXml(parser));
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected tag: " + tag);
+                    return false;
+            }
+            return true;
+        }
+    }
+
+    public static class Injector {
+        File environmentGetDataSystemDirectory() {
+            return Environment.getDataSystemDirectory();
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index db8da11..0595b73 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -26,6 +26,7 @@
 import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
@@ -56,6 +57,8 @@
 import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
 import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
 import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION;
@@ -161,6 +164,7 @@
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyDrawableResource;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.DeviceOwnerType;
@@ -177,6 +181,7 @@
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.NetworkEvent;
 import android.app.admin.ParcelableGranteeMap;
+import android.app.admin.ParcelableResource;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.PasswordPolicy;
 import android.app.admin.SecurityLog;
@@ -231,6 +236,7 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivitySettingsManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.ProfileNetworkPreference;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.net.VpnManager;
@@ -715,6 +721,8 @@
     // Guarded by mHandler
     private @UserIdInt int mNetworkLoggingNotificationUserId = UserHandle.USER_NULL;
 
+    private final DeviceManagementResourcesProvider mDeviceManagementResourcesProvider;
+
     private static final boolean ENABLE_LOCK_GUARD = true;
 
     /**
@@ -1739,6 +1747,10 @@
         void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
             mSafetyChecker = safetyChecker;
         }
+
+        DeviceManagementResourcesProvider getDeviceManagementResourcesProvider() {
+            return new DeviceManagementResourcesProvider();
+        }
     }
 
     /**
@@ -1791,6 +1803,8 @@
         mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
         mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
 
+        mDeviceManagementResourcesProvider = mInjector.getDeviceManagementResourcesProvider();
+
         // "Lite" interface is available even when the device doesn't have the feature
         LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService);
         if (!mHasFeature) {
@@ -1837,6 +1851,8 @@
         loadOwners();
 
         performPolicyVersionUpgrade();
+
+        mDeviceManagementResourcesProvider.load();
     }
 
     /**
@@ -17827,10 +17843,14 @@
         }
         int networkPreference = preferentialNetworkServiceEnabled
                 ? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
+        ProfileNetworkPreference.Builder preferenceBuilder =
+                new ProfileNetworkPreference.Builder();
+        preferenceBuilder.setPreference(networkPreference);
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceBuilder.build());
         mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.getConnectivityManager().setProfileNetworkPreference(
-                        UserHandle.of(userId),
-                        networkPreference,
+                mInjector.getConnectivityManager().setProfileNetworkPreferences(
+                        UserHandle.of(userId), preferences,
                         null /* executor */, null /* listener */));
     }
 
@@ -17951,4 +17971,54 @@
                         && mInjector.getUsbManager().getUsbHalVersion() >= UsbManager.USB_HAL_V1_3
         );
     }
+
+    @Override
+    public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+        Objects.requireNonNull(drawables, "drawables must be provided.");
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mDeviceManagementResourcesProvider.updateDrawables(drawables)) {
+                sendDrawableUpdatedBroadcast(
+                        drawables.stream().mapToInt(d -> d.getDrawableId()).toArray());
+            }
+        });
+    }
+
+    @Override
+    public void resetDrawables(@NonNull int[] drawableIds) {
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+        Objects.requireNonNull(drawableIds, "drawableIds must be provided.");
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (mDeviceManagementResourcesProvider.removeDrawables(drawableIds)) {
+                sendDrawableUpdatedBroadcast(drawableIds);
+            }
+        });
+    }
+
+    @Override
+    public ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource) {
+        return mInjector.binderWithCleanCallingIdentity(() ->
+                mDeviceManagementResourcesProvider.getDrawable(
+                        drawableId, drawableStyle, drawableSource));
+    }
+
+    private void sendDrawableUpdatedBroadcast(int[] drawableIds) {
+        final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
+        intent.putExtra(EXTRA_RESOURCE_ID, drawableIds);
+        intent.putExtra(EXTRA_RESOURCE_TYPE_DRAWABLE, /* value= */ true);
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+        List<UserInfo> users = mUserManager.getAliveUsers();
+        for (int i = 0; i < users.size(); i++) {
+            UserHandle user = users.get(i).getUserHandle();
+            mContext.sendBroadcastAsUser(intent, user);
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5fcee9b..c767f4d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -260,10 +260,6 @@
             "com.android.server.companion.virtual.VirtualDeviceManagerService";
     private static final String STATS_COMPANION_APEX_PATH =
             "/apex/com.android.os.statsd/javalib/service-statsd.jar";
-    private static final String SCHEDULING_APEX_PATH =
-            "/apex/com.android.scheduling/javalib/service-scheduling.jar";
-    private static final String REBOOT_READINESS_LIFECYCLE_CLASS =
-            "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle";
     private static final String CONNECTIVITY_SERVICE_APEX_PATH =
             "/apex/com.android.tethering/javalib/service-connectivity.jar";
     private static final String STATS_COMPANION_LIFECYCLE_CLASS =
@@ -2546,12 +2542,6 @@
                 STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH);
         t.traceEnd();
 
-        // Reboot Readiness
-        t.traceBegin("StartRebootReadinessManagerService");
-        mSystemServiceManager.startServiceFromJar(
-                REBOOT_READINESS_LIFECYCLE_CLASS, SCHEDULING_APEX_PATH);
-        t.traceEnd();
-
         // Statsd pulled atoms
         t.traceBegin("StartStatsPullAtomService");
         mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS);
@@ -2676,7 +2666,7 @@
         t.traceBegin("MakePowerManagerServiceReady");
         try {
             // TODO: use boot phase
-            mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
+            mPowerManagerService.systemReady();
         } catch (Throwable e) {
             reportWtf("making Power Manager Service ready", e);
         }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index d7e3195..8cf6fe3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -130,6 +130,7 @@
         AndroidPackage::getLabelRes,
         AndroidPackage::getLargestWidthLimitDp,
         AndroidPackage::getLogo,
+        AndroidPackage::getLocaleConfigRes,
         AndroidPackage::getManageSpaceActivityName,
         AndroidPackage::getMemtagMode,
         AndroidPackage::getMinSdkVersion,
diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp
index 01e90a8..a6ed1ae 100644
--- a/services/tests/apexsystemservices/Android.bp
+++ b/services/tests/apexsystemservices/Android.bp
@@ -37,5 +37,8 @@
         "truth-prebuilt",
         "modules-utils-build-testing",
     ],
-    test_suites: ["device-tests"],
+    test_suites: [
+        "device-tests",
+        "mts-core",
+    ],
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
index 5a6275d..cc97b8f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
@@ -24,7 +24,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.same;
@@ -226,7 +225,7 @@
     }
 
     private void expectWipeNonSystemUser() {
-        when(mUserManager.removeUserOrSetEphemeral(anyInt(), anyBoolean()))
+        when(mUserManager.removeUserWhenPossible(any(), anyBoolean()))
                 .thenReturn(UserManager.REMOVE_RESULT_REMOVED);
     }
 
@@ -266,7 +265,7 @@
     }
 
     private void verifyWipeNonSystemUser() {
-        verify(mUserManager).removeUserOrSetEphemeral(anyInt(), anyBoolean());
+        verify(mUserManager).removeUserWhenPossible(any(), anyBoolean());
     }
 
     private void setPendingResultForUser(int userId) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index edf6816..1a5888e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -183,7 +183,8 @@
                 mOverridesToRemoveByPackageConfigCaptor.getValue().packageNameToOverridesToRemove;
         Map<Long, PackageOverride> addedOverrides;
         assertThat(packageNameToAddedOverrides.keySet()).containsExactly(PACKAGE_1, PACKAGE_3);
-        assertThat(packageNameToRemovedOverrides.keySet()).containsExactly(PACKAGE_3, PACKAGE_4);
+        assertThat(packageNameToRemovedOverrides.keySet()).containsExactly(PACKAGE_2, PACKAGE_3,
+                PACKAGE_4);
         // Package 1
         addedOverrides = packageNameToAddedOverrides.get(PACKAGE_1).overrides;
         assertThat(addedOverrides).hasSize(3);
@@ -193,6 +194,9 @@
                 new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
         assertThat(addedOverrides.get(789L)).isEqualTo(
                 new PackageOverride.Builder().setEnabled(false).build());
+        // Package 2
+        assertThat(packageNameToRemovedOverrides.get(PACKAGE_2).changeIds).containsExactly(123L,
+                456L, 789L);
         // Package 3
         addedOverrides = packageNameToAddedOverrides.get(PACKAGE_3).overrides;
         assertThat(addedOverrides).hasSize(1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
new file mode 100644
index 0000000..52d0494
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppGlobals;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
+import android.os.BatteryManagerInternal;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryControllerTest {
+    private static final int CALLING_UID = 1000;
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+    private static final int SOURCE_USER_ID = 0;
+
+    private BatteryController mBatteryController;
+    private BroadcastReceiver mPowerReceiver;
+    private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
+    private int mSourceUid;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private Context mContext;
+    @Mock
+    private BatteryManagerInternal mBatteryManagerInternal;
+    @Mock
+    private JobSchedulerService mJobSchedulerService;
+    @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+
+        // Called in StateController constructor.
+        when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+        when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        // Called in BatteryController constructor.
+        doReturn(mBatteryManagerInternal)
+                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+        // Used in JobStatus.
+        doReturn(mPackageManagerInternal)
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
+        // Initialize real objects.
+        // Capture the listeners.
+        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        mBatteryController = new BatteryController(mJobSchedulerService);
+
+        verify(mContext).registerReceiver(receiverCaptor.capture(),
+                ArgumentMatchers.argThat(filter ->
+                        filter.hasAction(Intent.ACTION_POWER_CONNECTED)
+                                && filter.hasAction(Intent.ACTION_POWER_DISCONNECTED)));
+        mPowerReceiver = receiverCaptor.getValue();
+        try {
+            mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
+            // Need to do this since we're using a mock JS and not a real object.
+            doReturn(new ArraySet<>(new String[]{SOURCE_PACKAGE}))
+                    .when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+        setPowerConnected(false);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private void setBatteryNotLow(boolean notLow) {
+        doReturn(notLow).when(mJobSchedulerService).isBatteryNotLow();
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onBatteryStateChangedLocked();
+        }
+        waitForNonDelayedMessagesProcessed();
+    }
+
+    private void setCharging() {
+        doReturn(true).when(mJobSchedulerService).isBatteryCharging();
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onBatteryStateChangedLocked();
+        }
+        waitForNonDelayedMessagesProcessed();
+    }
+
+    private void setDischarging() {
+        doReturn(false).when(mJobSchedulerService).isBatteryCharging();
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onBatteryStateChangedLocked();
+        }
+        waitForNonDelayedMessagesProcessed();
+    }
+
+    private void setPowerConnected(boolean connected) {
+        Intent intent = new Intent(
+                connected ? Intent.ACTION_POWER_CONNECTED : Intent.ACTION_POWER_DISCONNECTED);
+        mPowerReceiver.onReceive(mContext, intent);
+    }
+
+    private void setUidBias(int uid, int bias) {
+        int prevBias = mJobSchedulerService.getUidBias(uid);
+        doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.onUidBiasChangedLocked(uid, prevBias, bias);
+        }
+    }
+
+    private void trackJobs(JobStatus... jobs) {
+        for (JobStatus job : jobs) {
+            synchronized (mBatteryController.mLock) {
+                mBatteryController.maybeStartTrackingJobLocked(job, null);
+            }
+        }
+    }
+
+    private void waitForNonDelayedMessagesProcessed() {
+        JobSchedulerBackgroundThread.getHandler().runWithScissors(() -> {}, 15_000);
+    }
+
+    private JobInfo.Builder createBaseJobInfoBuilder(int jobId) {
+        return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestBatteryJobService"));
+    }
+
+    private JobInfo.Builder createBaseJobInfoBuilder(int jobId, String pkgName) {
+        return new JobInfo.Builder(jobId, new ComponentName(pkgName, "TestBatteryJobService"));
+    }
+
+    private JobStatus createJobStatus(String testTag, String packageName, int callingUid,
+            JobInfo jobInfo) {
+        JobStatus js = JobStatus.createFromJobInfo(
+                jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
+        js.serviceInfo = mock(ServiceInfo.class);
+        // Make sure tests aren't passing just because the default bucket is likely ACTIVE.
+        js.setStandbyBucket(FREQUENT_INDEX);
+        return js;
+    }
+
+    @Test
+    public void testBatteryNotLow() {
+        JobStatus job1 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(1).setRequiresBatteryNotLow(true).build());
+        JobStatus job2 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(2).setRequiresBatteryNotLow(true).build());
+
+        setBatteryNotLow(false);
+        trackJobs(job1);
+        assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+
+        setBatteryNotLow(true);
+        assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+
+        trackJobs(job2);
+        assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
+    }
+
+    @Test
+    public void testCharging_BatteryNotLow() {
+        JobStatus job1 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(1)
+                        .setRequiresCharging(true)
+                        .setRequiresBatteryNotLow(true).build());
+        JobStatus job2 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
+                createBaseJobInfoBuilder(2)
+                        .setRequiresCharging(true)
+                        .setRequiresBatteryNotLow(false).build());
+
+        setBatteryNotLow(true);
+        setDischarging();
+        trackJobs(job1, job2);
+        assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        setCharging();
+        assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+    }
+
+    @Test
+    public void testTopPowerConnectedExemption() {
+        final int uid1 = mSourceUid;
+        final int uid2 = mSourceUid + 1;
+        final int uid3 = mSourceUid + 2;
+        JobStatus jobFg = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
+                createBaseJobInfoBuilder(1).setRequiresCharging(true).build());
+        JobStatus jobFgRunner = createJobStatus("testTopPowerConnectedExemption",
+                SOURCE_PACKAGE, uid1,
+                createBaseJobInfoBuilder(2).setRequiresCharging(true).build());
+        JobStatus jobFgLow = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
+                createBaseJobInfoBuilder(3)
+                        .setRequiresCharging(true)
+                        .setPriority(JobInfo.PRIORITY_LOW)
+                        .build());
+        JobStatus jobBg = createJobStatus("testTopPowerConnectedExemption",
+                "some.background.app", uid2,
+                createBaseJobInfoBuilder(4, "some.background.app")
+                        .setRequiresCharging(true)
+                        .build());
+        JobStatus jobLateFg = createJobStatus("testTopPowerConnectedExemption",
+                "switch.to.fg", uid3,
+                createBaseJobInfoBuilder(5, "switch.to.fg").setRequiresCharging(true).build());
+        JobStatus jobLateFgLow = createJobStatus("testTopPowerConnectedExemption",
+                "switch.to.fg", uid3,
+                createBaseJobInfoBuilder(6, "switch.to.fg")
+                        .setRequiresCharging(true)
+                        .setPriority(JobInfo.PRIORITY_MIN)
+                        .build());
+
+        setBatteryNotLow(false);
+        setDischarging();
+        setUidBias(uid1, JobInfo.BIAS_TOP_APP);
+        setUidBias(uid2, JobInfo.BIAS_DEFAULT);
+        setUidBias(uid3, JobInfo.BIAS_DEFAULT);
+
+        // Jobs are scheduled when power isn't connected.
+        setPowerConnected(false);
+        trackJobs(jobFg, jobFgLow, jobBg, jobLateFg, jobLateFgLow);
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        // Power is connected. TOP app should be allowed to start job DEFAULT+ jobs.
+        setPowerConnected(true);
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        // Test that newly scheduled job of TOP app is correctly allowed to run.
+        trackJobs(jobFgRunner);
+        assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        // Switch top app. New TOP app should be allowed to run job and the running job of
+        // previously TOP app should be allowed to continue to run.
+        synchronized (mBatteryController.mLock) {
+            mBatteryController.prepareForExecutionLocked(jobFgRunner);
+        }
+        setUidBias(uid1, JobInfo.BIAS_DEFAULT);
+        setUidBias(uid2, JobInfo.BIAS_DEFAULT);
+        setUidBias(uid3, JobInfo.BIAS_TOP_APP);
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertTrue(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+
+        setPowerConnected(false);
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+        assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 95912b2..d741459 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -200,8 +200,10 @@
     }
 
     private void setUidBias(int uid, int bias) {
+        int prevBias = mJobSchedulerService.getUidBias(uid);
+        doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
         synchronized (mPrefetchController.mLock) {
-            mPrefetchController.onUidBiasChangedLocked(uid, bias);
+            mPrefetchController.onUidBiasChangedLocked(uid, prevBias, bias);
         }
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
index b65c3e9..0411b94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -263,7 +263,7 @@
     @Test
     public void testUserActivityOnDeviceStateChange() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         final DisplayInfo info = new DisplayInfo();
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index c3a364e..171af77 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -25,6 +25,7 @@
         "test-apps/JobTestApp/src/**/*.java",
 
         "test-apps/SuspendTestApp/src/**/*.java",
+        ":service-bluetooth-tests-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready
     ],
     static_libs: [
         "frameworks-base-testutils",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 202a54d..587447a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -95,6 +95,7 @@
     <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
     <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
     <uses-permission android:name="android.permission.KILL_UID"/>
+    <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
     <uses-permission
         android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
deleted file mode 100644
index a1d4c20..0000000
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.server;
-
-import static org.mockito.Mockito.*;
-
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.os.Looper;
-import android.provider.Settings;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class BluetoothAirplaneModeListenerTest {
-    private Context mContext;
-    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
-    private BluetoothAdapter mBluetoothAdapter;
-    private BluetoothModeChangeHelper mHelper;
-
-    @Mock BluetoothManagerService mBluetoothManagerService;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-
-        mHelper = mock(BluetoothModeChangeHelper.class);
-        when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
-                .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
-        doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
-        doNothing().when(mHelper).showToastMessage();
-        doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class));
-
-        mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
-                    mBluetoothManagerService, Looper.getMainLooper(), mContext);
-        mBluetoothAirplaneModeListener.start(mHelper);
-    }
-
-    @Test
-    public void testIgnoreOnAirplanModeChange() {
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
-        when(mHelper.isBluetoothOn()).thenReturn(true);
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
-        when(mHelper.isMediaProfileConnected()).thenReturn(true);
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-
-        when(mHelper.isAirplaneModeOn()).thenReturn(true);
-        Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
-    }
-
-    @Test
-    public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() {
-        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-        verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService);
-    }
-
-    @Test
-    public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
-        when(mHelper.isBluetoothOn()).thenReturn(true);
-        when(mHelper.isMediaProfileConnected()).thenReturn(true);
-        when(mHelper.isAirplaneModeOn()).thenReturn(true);
-        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-
-        verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
-                BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
-        verify(mHelper, times(0)).showToastMessage();
-        verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
-    }
-
-    @Test
-    public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = 0;
-        when(mHelper.isBluetoothOn()).thenReturn(true);
-        when(mHelper.isMediaProfileConnected()).thenReturn(true);
-        when(mHelper.isAirplaneModeOn()).thenReturn(true);
-        mBluetoothAirplaneModeListener.handleAirplaneModeChange();
-
-        verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
-                BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
-        verify(mHelper).showToastMessage();
-        verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
-    }
-
-    @Test
-    public void testIsPopToast_PopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = 0;
-        Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast());
-        verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1);
-    }
-
-    @Test
-    public void testIsPopToast_NotPopToast() {
-        mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
-        Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast());
-        verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index f1a63bc..6818d1f 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,13 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 
@@ -64,6 +71,7 @@
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
 import android.os.Binder;
@@ -617,6 +625,100 @@
         assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true);
     }
 
+    /** Tests handleIncomingUser() for a variety of permissions and situations. */
+    @Test
+    public void testHandleIncomingUser() throws Exception {
+        final UserInfo user1a = new UserInfo(111, "user1a", 0);
+        final UserInfo user1b = new UserInfo(112, "user1b", 0);
+        final UserInfo user2 = new UserInfo(113, "user2", 0);
+        // user1a and user2b are in the same profile group; user2 is in a different one.
+        user1a.profileGroupId = 5;
+        user1b.profileGroupId = 5;
+        user2.profileGroupId = 6;
+
+        final List<UserInfo> users = Arrays.asList(user1a, user1b, user2);
+        when(mInjector.mUserManagerMock.getUsers(false)).thenReturn(users);
+        mUserController.onSystemReady(); // To set the profileGroupIds in UserController.
+
+
+        // Has INTERACT_ACROSS_USERS_FULL.
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkPermissionForPreflight(
+                eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(false);
+
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, true);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, true);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+
+        // Has INTERACT_ACROSS_USERS.
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mInjector.checkPermissionForPreflight(
+                eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(false);
+
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user2.id,  ALLOW_NON_FULL_IN_PROFILE, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, true);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+
+        // Has INTERACT_ACROSS_PROFILES.
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkComponentPermission(
+                eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mInjector.checkPermissionForPreflight(
+                eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(true);
+
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, false);
+
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, false);
+        checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+    }
+
+    private void checkHandleIncomingUser(int fromUser, int toUser, int allowMode, boolean pass) {
+        final int pid = 100;
+        final int uid = fromUser * UserHandle.PER_USER_RANGE + 34567 + fromUser;
+        final String name = "whatever";
+        final String pkg = "some.package";
+        final boolean allowAll = false;
+
+        if (pass) {
+            mUserController.handleIncomingUser(pid, uid, toUser, allowAll, allowMode, name, pkg);
+        } else {
+            assertThrows(SecurityException.class, () -> mUserController.handleIncomingUser(
+                    pid, uid, toUser, allowAll, allowMode, name, pkg));
+        }
+    }
+
     private void setUpAndStartUserInBackground(int userId) throws Exception {
         setUpUser(userId, 0);
         mUserController.startUser(userId, /* foreground= */ false);
@@ -784,6 +886,23 @@
         }
 
         @Override
+        int checkComponentPermission(String permission, int pid, int uid, int owner, boolean exp) {
+            Log.i(TAG, "checkComponentPermission " + permission);
+            return PERMISSION_GRANTED;
+        }
+
+        @Override
+        boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
+            Log.i(TAG, "checkPermissionForPreflight " + permission);
+            return true;
+        }
+
+        @Override
+        boolean isCallerRecents(int uid) {
+            return false;
+        }
+
+        @Override
         WindowManagerService getWindowManager() {
             return mWindowManagerMock;
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
new file mode 100644
index 0000000..d4bac2c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static android.testing.TestableLooper.RunWithLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class BiometricSchedulerOperationTest {
+
+    public interface FakeHal {}
+    public abstract static class InterruptableMonitor<T>
+            extends HalClientMonitor<T> implements  Interruptable {
+        public InterruptableMonitor() {
+            super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+        }
+    }
+
+    @Mock
+    private InterruptableMonitor<FakeHal> mClientMonitor;
+    @Mock
+    private BaseClientMonitor.Callback mClientCallback;
+    @Mock
+    private FakeHal mHal;
+    @Captor
+    ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback;
+
+    private Handler mHandler;
+    private BiometricSchedulerOperation mOperation;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mHandler = new Handler(TestableLooper.get(this).getLooper());
+        mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
+    }
+
+    @Test
+    public void testStartWithCookie() {
+        final int cookie = 200;
+        when(mClientMonitor.getCookie()).thenReturn(cookie);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        assertThat(mOperation.isReadyToStart()).isEqualTo(cookie);
+        assertThat(mOperation.isStarted()).isFalse();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+
+        final boolean started = mOperation.startWithCookie(
+                mock(BaseClientMonitor.Callback.class), cookie);
+
+        assertThat(started).isTrue();
+        verify(mClientMonitor).start(mStartCallback.capture());
+        mStartCallback.getValue().onClientStarted(mClientMonitor);
+        assertThat(mOperation.isStarted()).isTrue();
+    }
+
+    @Test
+    public void testNoStartWithoutCookie() {
+        final int goodCookie = 20;
+        final int badCookie = 22;
+        when(mClientMonitor.getCookie()).thenReturn(goodCookie);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
+        final boolean started = mOperation.startWithCookie(
+                mock(BaseClientMonitor.Callback.class), badCookie);
+
+        assertThat(started).isFalse();
+        assertThat(mOperation.isStarted()).isFalse();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+    }
+
+    @Test
+    public void startsWhenReadyAndHalAvailable() {
+        when(mClientMonitor.getCookie()).thenReturn(0);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+        mOperation.start(cb);
+        verify(mClientMonitor).start(mStartCallback.capture());
+        mStartCallback.getValue().onClientStarted(mClientMonitor);
+
+        assertThat(mOperation.isStarted()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+
+        verify(mClientCallback).onClientStarted(eq(mClientMonitor));
+        verify(cb).onClientStarted(eq(mClientMonitor));
+        verify(mClientCallback, never()).onClientFinished(any(), anyBoolean());
+        verify(cb, never()).onClientFinished(any(), anyBoolean());
+
+        mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+        assertThat(mOperation.isFinished()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        verify(mClientMonitor).destroy();
+        verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+    }
+
+    @Test
+    public void startFailsWhenReadyButHalNotAvailable() {
+        when(mClientMonitor.getCookie()).thenReturn(0);
+        when(mClientMonitor.getFreshDaemon()).thenReturn(null);
+
+        final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+        mOperation.start(cb);
+        verify(mClientMonitor, never()).start(any());
+
+        assertThat(mOperation.isStarted()).isFalse();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isTrue();
+
+        verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor));
+        verify(cb, never()).onClientStarted(eq(mClientMonitor));
+        verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false));
+        verify(cb).onClientFinished(eq(mClientMonitor), eq(false));
+    }
+
+    @Test
+    public void doesNotStartWithCookie() {
+        when(mClientMonitor.getCookie()).thenReturn(9);
+        assertThrows(IllegalStateException.class,
+                () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+    }
+
+    @Test
+    public void cannotRestart() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+        assertThrows(IllegalStateException.class,
+                () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+    }
+
+    @Test
+    public void abortsNotRunning() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.abort();
+
+        assertThat(mOperation.isFinished()).isTrue();
+        verify(mClientMonitor).unableToStart();
+        verify(mClientMonitor).destroy();
+        assertThrows(IllegalStateException.class,
+                () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+    }
+
+    @Test
+    public void cannotAbortRunning() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+        assertThrows(IllegalStateException.class, () -> mOperation.abort());
+    }
+
+    @Test
+    public void cancel() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class);
+        final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+        mOperation.start(startCb);
+        verify(mClientMonitor).start(mStartCallback.capture());
+        mStartCallback.getValue().onClientStarted(mClientMonitor);
+        mOperation.cancel(mHandler, cancelCb);
+
+        assertThat(mOperation.isCanceling()).isTrue();
+        verify(mClientMonitor).cancel();
+        verify(mClientMonitor, never()).cancelWithoutStarting(any());
+        verify(mClientMonitor, never()).destroy();
+
+        mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+        assertThat(mOperation.isFinished()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        verify(mClientMonitor).destroy();
+
+        // should be unused since the operation was started
+        verify(cancelCb, never()).onClientStarted(any());
+        verify(cancelCb, never()).onClientFinished(any(), anyBoolean());
+    }
+
+    @Test
+    public void cancelWithoutStarting() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+        mOperation.cancel(mHandler, cancelCb);
+
+        assertThat(mOperation.isCanceling()).isTrue();
+        ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor =
+                ArgumentCaptor.forClass(BaseClientMonitor.Callback.class);
+        verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
+
+        cbCaptor.getValue().onClientFinished(mClientMonitor, true);
+        verify(cancelCb).onClientFinished(eq(mClientMonitor), eq(true));
+        verify(mClientMonitor, never()).start(any());
+        verify(mClientMonitor, never()).cancel();
+        verify(mClientMonitor).destroy();
+    }
+
+    @Test
+    public void markCanceling() {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.markCanceling();
+
+        assertThat(mOperation.isMarkedCanceling()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        assertThat(mOperation.isFinished()).isFalse();
+        verify(mClientMonitor, never()).start(any());
+        verify(mClientMonitor, never()).cancel();
+        verify(mClientMonitor, never()).cancelWithoutStarting(any());
+        verify(mClientMonitor, never()).unableToStart();
+        verify(mClientMonitor, never()).destroy();
+    }
+
+    @Test
+    public void cancelPendingWithCookie() {
+        markCancellingAndStart(2);
+    }
+
+    @Test
+    public void cancelPendingWithoutCookie() {
+        markCancellingAndStart(null);
+    }
+
+    private void markCancellingAndStart(Integer withCookie) {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+        if (withCookie != null) {
+            when(mClientMonitor.getCookie()).thenReturn(withCookie);
+        }
+
+        mOperation.markCanceling();
+        final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+        if (withCookie != null) {
+            mOperation.startWithCookie(cb, withCookie);
+        } else {
+            mOperation.start(cb);
+        }
+
+        assertThat(mOperation.isFinished()).isTrue();
+        verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+        verify(mClientMonitor, never()).start(any());
+        verify(mClientMonitor, never()).cancel();
+        verify(mClientMonitor, never()).cancelWithoutStarting(any());
+        verify(mClientMonitor, never()).unableToStart();
+        verify(mClientMonitor).destroy();
+    }
+
+    @Test
+    public void cancelWatchdogWhenStarted() {
+        cancelWatchdog(true);
+    }
+
+    @Test
+    public void cancelWatchdogWithoutStarting() {
+        cancelWatchdog(false);
+    }
+
+    private void cancelWatchdog(boolean start) {
+        when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+        mOperation.start(mock(BaseClientMonitor.Callback.class));
+        if (start) {
+            verify(mClientMonitor).start(mStartCallback.capture());
+            mStartCallback.getValue().onClientStarted(mClientMonitor);
+        }
+        mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class));
+
+        assertThat(mOperation.isCanceling()).isTrue();
+
+        // omit call to onClientFinished and trigger watchdog
+        mOperation.mCancelWatchdog.run();
+
+        assertThat(mOperation.isFinished()).isTrue();
+        assertThat(mOperation.isCanceling()).isFalse();
+        verify(mClientMonitor).destroy();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index d192697..ac08319 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -16,10 +16,14 @@
 
 package com.android.server.biometrics.sensors;
 
+import static android.testing.TestableLooper.RunWithLooper;
+
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,10 +38,13 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.IBiometricService;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
+import android.testing.TestableLooper;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -46,16 +53,18 @@
 
 import com.android.server.biometrics.nano.BiometricSchedulerProto;
 import com.android.server.biometrics.nano.BiometricsProto;
-import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @Presubmit
 @SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
 public class BiometricSchedulerTest {
 
     private static final String TAG = "BiometricSchedulerTest";
@@ -76,8 +85,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mToken = new Binder();
-        mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
-                null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
+        mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
+                BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
+                mBiometricService, LOG_NUM_RECENT_OPERATIONS,
                 CoexCoordinator.getInstance());
     }
 
@@ -86,9 +96,9 @@
         final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
 
         final HalClientMonitor<Object> client1 =
-                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+                new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
         final HalClientMonitor<Object> client2 =
-                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+                new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
         mScheduler.scheduleClientMonitor(client1);
         mScheduler.scheduleClientMonitor(client2);
 
@@ -99,20 +109,17 @@
     @Test
     public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
         // Even if second client has a non-null daemon, it needs to be canceled.
-        Object daemon2 = mock(Object.class);
-
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
-        final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1);
-        final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+        final TestHalClientMonitor client1 = new TestHalClientMonitor(
+                mContext, mToken, () -> null);
+        final TestHalClientMonitor client2 = new TestHalClientMonitor(
+                mContext, mToken, () -> mock(Object.class));
 
         final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
         final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
 
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
-        mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+        mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
                 mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
@@ -122,11 +129,11 @@
         mScheduler.scheduleClientMonitor(client2, callback2);
         waitForIdle();
 
-        assertTrue(client1.wasUnableToStart());
+        assertTrue(client1.mUnableToStart);
         verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
         verify(callback1, never()).onClientStarted(any());
 
-        assertTrue(client2.wasUnableToStart());
+        assertTrue(client2.mUnableToStart);
         verify(callback2).onClientFinished(eq(client2), eq(false) /* success */);
         verify(callback2, never()).onClientStarted(any());
 
@@ -138,21 +145,19 @@
         // Second non-BiometricPrompt client has a valid daemon
         final Object daemon2 = mock(Object.class);
 
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
         final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
 
         final TestAuthenticationClient client1 =
-                new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
-        final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+                new TestAuthenticationClient(mContext, () -> null, mToken, listener1);
+        final TestHalClientMonitor client2 =
+                new TestHalClientMonitor(mContext, mToken, () -> daemon2);
 
         final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
         final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
 
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
-        mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+        mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
                 mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
@@ -172,8 +177,8 @@
         verify(callback1, never()).onClientStarted(any());
 
         // Client 2 was able to start
-        assertFalse(client2.wasUnableToStart());
-        assertTrue(client2.hasStarted());
+        assertFalse(client2.mUnableToStart);
+        assertTrue(client2.mStarted);
         verify(callback2).onClientStarted(eq(client2));
     }
 
@@ -187,16 +192,18 @@
         // Schedule a BiometricPrompt authentication request
         mScheduler.scheduleClientMonitor(client1, callback1);
 
-        assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState);
-        assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor);
+        assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
+        assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
         assertEquals(0, mScheduler.mPendingOperations.size());
 
         // Request it to be canceled. The operation can be canceled immediately, and the scheduler
         // should go back to idle, since in this case the framework has not even requested the HAL
         // to authenticate yet.
         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+        waitForIdle();
         assertTrue(client1.isAlreadyDone());
         assertTrue(client1.mDestroyed);
+        assertFalse(client1.mStartedHal);
         assertNull(mScheduler.mCurrentOperation);
     }
 
@@ -210,8 +217,8 @@
         // assertEquals(0, bsp.recentOperations.length);
 
         // Pretend the scheduler is busy enrolling, and check the proto dump again.
-        final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+        final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
         mScheduler.scheduleClientMonitor(client);
         waitForIdle();
         bsp = getDump(true /* clearSchedulerBuffer */);
@@ -230,8 +237,8 @@
     @Test
     public void testProtoDump_fifo() throws Exception {
         // Add the first operation
-        final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+        final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
         mScheduler.scheduleClientMonitor(client);
         waitForIdle();
         BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
@@ -244,8 +251,8 @@
         client.getCallback().onClientFinished(client, true);
 
         // Add another operation
-        final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_REMOVE);
+        final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE);
         mScheduler.scheduleClientMonitor(client2);
         waitForIdle();
         bsp = getDump(false /* clearSchedulerBuffer */);
@@ -256,8 +263,8 @@
         client2.getCallback().onClientFinished(client2, true);
 
         // And another operation
-        final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
-                () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
+        final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken,
+                () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE);
         mScheduler.scheduleClientMonitor(client3);
         waitForIdle();
         bsp = getDump(false /* clearSchedulerBuffer */);
@@ -290,8 +297,7 @@
     @Test
     public void testCancelPendingAuth() throws RemoteException {
         final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-
-        final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon);
+        final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
         final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
         final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
                 mToken, callback);
@@ -302,14 +308,12 @@
         waitForIdle();
 
         assertEquals(mScheduler.getCurrentClient(), client1);
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
 
         // Request cancel before the authentication client has started
         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
         waitForIdle();
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
 
         // Finish the blocking client. The authentication client should send ERROR_CANCELED
         client1.getCallback().onClientFinished(client1, true /* success */);
@@ -326,67 +330,109 @@
 
     @Test
     public void testCancels_whenAuthRequestIdNotSet() {
-        testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+        testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */);
     }
 
     @Test
     public void testCancels_whenAuthRequestIdNotSet_notStarted() {
-        testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+        testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */);
     }
 
     @Test
     public void testCancels_whenAuthRequestIdMatches() {
-        testCancelsWhenRequestId(200L, 200, true /* started */);
+        testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */);
     }
 
     @Test
     public void testCancels_whenAuthRequestIdMatches_noStarted() {
-        testCancelsWhenRequestId(200L, 200, false /* started */);
+        testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */);
     }
 
     @Test
     public void testDoesNotCancel_whenAuthRequestIdMismatched() {
-        testCancelsWhenRequestId(10L, 20, true /* started */);
+        testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */);
     }
 
     @Test
     public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
-        testCancelsWhenRequestId(10L, 20, false /* started */);
+        testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */);
+    }
+
+    private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+            boolean started) {
+        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+        testCancelsWhenRequestId(requestId, cancelRequestId, started,
+                new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback));
+    }
+
+    @Test
+    public void testCancels_whenEnrollRequestIdNotSet() {
+        testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */);
+    }
+
+    @Test
+    public void testCancels_whenEnrollRequestIdMatches() {
+        testCancelsEnrollWhenRequestId(200L, 200, false /* started */);
+    }
+
+    @Test
+    public void testDoesNotCancel_whenEnrollRequestIdMismatched() {
+        testCancelsEnrollWhenRequestId(10L, 20, false /* started */);
+    }
+
+    private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+            boolean started) {
+        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+        testCancelsWhenRequestId(requestId, cancelRequestId, started,
+                new TestEnrollClient(mContext, lazyDaemon, mToken, callback));
     }
 
     private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
-            boolean started) {
+            boolean started, HalClientMonitor<?> client) {
         final boolean matches = requestId == null || requestId == cancelRequestId;
-        final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-        final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
-        final TestAuthenticationClient client = new TestAuthenticationClient(
-                mContext, lazyDaemon, mToken, callback);
         if (requestId != null) {
             client.setRequestId(requestId);
         }
 
+        final boolean isAuth = client instanceof TestAuthenticationClient;
+        final boolean isEnroll = client instanceof TestEnrollClient;
+
         mScheduler.scheduleClientMonitor(client);
         if (started) {
             mScheduler.startPreparedClient(client.getCookie());
         }
         waitForIdle();
-        mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+        if (isAuth) {
+            mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+        } else if (isEnroll) {
+            mScheduler.cancelEnrollment(mToken, cancelRequestId);
+        } else {
+            fail("unexpected operation type");
+        }
         waitForIdle();
 
-        assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+        if (isAuth) {
+            // auth clients that were waiting for cookie when canceled should never invoke the hal
+            final TestAuthenticationClient authClient = (TestAuthenticationClient) client;
+            assertEquals(matches && started ? 1 : 0, authClient.mNumCancels);
+            assertEquals(started, authClient.mStartedHal);
+        } else if (isEnroll) {
+            final TestEnrollClient enrollClient = (TestEnrollClient) client;
+            assertEquals(matches ? 1 : 0, enrollClient.mNumCancels);
+            assertTrue(enrollClient.mStartedHal);
+        }
 
         if (matches) {
-            if (started) {
-                assertEquals(Operation.STATE_STARTED_CANCELING,
-                        mScheduler.mCurrentOperation.mState);
+            if (started || isEnroll) { // prep'd auth clients and enroll clients
+                assertTrue(mScheduler.mCurrentOperation.isCanceling());
             }
         } else {
-            if (started) {
-                assertEquals(Operation.STATE_STARTED,
-                        mScheduler.mCurrentOperation.mState);
+            if (started || isEnroll) { // prep'd auth clients and enroll clients
+                assertTrue(mScheduler.mCurrentOperation.isStarted());
             } else {
-                assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
-                        mScheduler.mCurrentOperation.mState);
+                assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
             }
         }
     }
@@ -411,18 +457,14 @@
         mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
         waitForIdle();
 
-        assertEquals(Operation.STATE_STARTED,
-                mScheduler.mCurrentOperation.mState);
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertTrue(mScheduler.mCurrentOperation.isStarted());
+        assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
 
         mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
         waitForIdle();
 
-        assertEquals(Operation.STATE_STARTED,
-                mScheduler.mCurrentOperation.mState);
-        assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
-                mScheduler.mPendingOperations.getFirst().mState);
+        assertTrue(mScheduler.mCurrentOperation.isStarted());
+        assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
     }
 
     @Test
@@ -459,12 +501,12 @@
     @Test
     public void testClientDestroyed_afterFinish() {
         final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
-        final TestClientMonitor client =
-                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+        final TestHalClientMonitor client =
+                new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
         mScheduler.scheduleClientMonitor(client);
         client.mCallback.onClientFinished(client, true /* success */);
         waitForIdle();
-        assertTrue(client.wasDestroyed());
+        assertTrue(client.mDestroyed);
     }
 
     private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
@@ -472,8 +514,10 @@
     }
 
     private static class TestAuthenticationClient extends AuthenticationClient<Object> {
-        int mNumCancels = 0;
+        boolean mStartedHal = false;
+        boolean mStoppedHal = false;
         boolean mDestroyed = false;
+        int mNumCancels = 0;
 
         public TestAuthenticationClient(@NonNull Context context,
                 @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -488,18 +532,16 @@
 
         @Override
         protected void stopHalOperation() {
-
+            mStoppedHal = true;
         }
 
         @Override
         protected void startHalOperation() {
-
+            mStartedHal = true;
         }
 
         @Override
-        protected void handleLifecycleAfterAuth(boolean authenticated) {
-
-        }
+        protected void handleLifecycleAfterAuth(boolean authenticated) {}
 
         @Override
         public boolean wasUserDetected() {
@@ -519,36 +561,59 @@
         }
     }
 
-    private static class TestClientMonitor2 extends TestClientMonitor {
-        private final int mProtoEnum;
+    private static class TestEnrollClient extends EnrollClient<Object> {
+        boolean mStartedHal = false;
+        boolean mStoppedHal = false;
+        int mNumCancels = 0;
 
-        public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
-                @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
-            super(context, token, lazyDaemon);
-            mProtoEnum = protoEnum;
+        TestEnrollClient(@NonNull Context context,
+                @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
+                @NonNull ClientMonitorCallbackConverter listener) {
+            super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
+                    "test" /* owner */, mock(BiometricUtils.class),
+                    5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
+                    true /* shouldVibrate */);
         }
 
         @Override
-        public int getProtoEnum() {
-            return mProtoEnum;
+        protected void stopHalOperation() {
+            mStoppedHal = true;
+        }
+
+        @Override
+        protected void startHalOperation() {
+            mStartedHal = true;
+        }
+
+        @Override
+        protected boolean hasReachedEnrollmentLimit() {
+            return false;
+        }
+
+        @Override
+        public void cancel() {
+            mNumCancels++;
+            super.cancel();
         }
     }
 
-    private static class TestClientMonitor extends HalClientMonitor<Object> {
+    private static class TestHalClientMonitor extends HalClientMonitor<Object> {
+        private final int mProtoEnum;
         private boolean mUnableToStart;
         private boolean mStarted;
         private boolean mDestroyed;
 
-        public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+        TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
                 @NonNull LazyDaemon<Object> lazyDaemon) {
-            this(context, token, lazyDaemon, 0 /* cookie */);
+            this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER);
         }
 
-        public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
-                @NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
+        TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
+                @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) {
             super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
                     TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
                     0 /* statsAction */, 0 /* statsClient */);
+            mProtoEnum = protoEnum;
         }
 
         @Override
@@ -559,9 +624,7 @@
 
         @Override
         public int getProtoEnum() {
-            // Anything other than CM_NONE, which is used to represent "idle". Tests that need
-            // real proto enums should use TestClientMonitor2
-            return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+            return mProtoEnum;
         }
 
         @Override
@@ -573,7 +636,7 @@
 
         @Override
         protected void startHalOperation() {
-
+            mStarted = true;
         }
 
         @Override
@@ -581,22 +644,9 @@
             super.destroy();
             mDestroyed = true;
         }
-
-        public boolean wasUnableToStart() {
-            return mUnableToStart;
-        }
-
-        public boolean hasStarted() {
-            return mStarted;
-        }
-
-        public boolean wasDestroyed() {
-            return mDestroyed;
-        }
-
     }
 
-    private static void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    private void waitForIdle() {
+        TestableLooper.get(this).processAllMessages();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 7fccd49..407f5fb 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics.sensors;
 
+import static android.testing.TestableLooper.RunWithLooper;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
@@ -28,52 +30,53 @@
 import android.content.Context;
 import android.hardware.biometrics.IBiometricService;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
 @SmallTest
 public class UserAwareBiometricSchedulerTest {
 
-    private static final String TAG = "BiometricSchedulerTest";
+    private static final String TAG = "UserAwareBiometricSchedulerTest";
     private static final int TEST_SENSOR_ID = 0;
 
+    private Handler mHandler;
     private UserAwareBiometricScheduler mScheduler;
-    private IBinder mToken;
+    private IBinder mToken = new Binder();
 
     @Mock
     private Context mContext;
     @Mock
     private IBiometricService mBiometricService;
 
-    private TestUserStartedCallback mUserStartedCallback;
-    private TestUserStoppedCallback mUserStoppedCallback;
+    private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
+    private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
     private int mCurrentUserId = UserHandle.USER_NULL;
-    private boolean mStartOperationsFinish;
-    private int mStartUserClientCount;
+    private boolean mStartOperationsFinish = true;
+    private int mStartUserClientCount = 0;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-
-        mToken = new Binder();
-        mStartOperationsFinish = true;
-        mStartUserClientCount = 0;
-        mUserStartedCallback = new TestUserStartedCallback();
-        mUserStoppedCallback = new TestUserStoppedCallback();
-
+        mHandler = new Handler(TestableLooper.get(this).getLooper());
         mScheduler = new UserAwareBiometricScheduler(TAG,
+                mHandler,
                 BiometricScheduler.SENSOR_TYPE_UNKNOWN,
                 null /* gestureAvailabilityDispatcher */,
                 mBiometricService,
@@ -117,7 +120,7 @@
         mCurrentUserId = UserHandle.USER_NULL;
         mStartOperationsFinish = false;
 
-        final BaseClientMonitor[] nextClients = new BaseClientMonitor[] {
+        final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
                 mock(BaseClientMonitor.class),
                 mock(BaseClientMonitor.class),
                 mock(BaseClientMonitor.class)
@@ -147,11 +150,11 @@
         waitForIdle();
 
         final TestStartUserClient startUserClient =
-                (TestStartUserClient) mScheduler.mCurrentOperation.mClientMonitor;
+                (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
         mScheduler.reset();
         assertNull(mScheduler.mCurrentOperation);
 
-        final BiometricScheduler.Operation fakeOperation = new BiometricScheduler.Operation(
+        final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
                 mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
         mScheduler.mCurrentOperation = fakeOperation;
         startUserClient.mCallback.onClientFinished(startUserClient, true);
@@ -194,8 +197,8 @@
         verify(nextClient).start(any());
     }
 
-    private static void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    private void waitForIdle() {
+        TestableLooper.get(this).processAllMessages();
     }
 
     private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index a13dff2..2718bf9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.CoexCoordinator;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -79,10 +80,13 @@
         when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
 
         mScheduler = new UserAwareBiometricScheduler(TAG,
+                new Handler(mLooper.getLooper()),
                 BiometricScheduler.SENSOR_TYPE_FACE,
                 null /* gestureAvailabilityDispatcher */,
+                mBiometricService,
                 () -> USER_ID,
-                mUserSwitchCallback);
+                mUserSwitchCallback,
+                CoexCoordinator.getInstance());
         mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
                 TAG, mScheduler, SENSOR_ID,
                 USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 39c51d5..21a7a8a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -32,7 +32,9 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 
@@ -69,6 +71,7 @@
     @Mock
     private BiometricScheduler mScheduler;
 
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
     private LockoutResetDispatcher mLockoutResetDispatcher;
     private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
     private IBinder mBinder;
@@ -97,7 +100,7 @@
                 resetLockoutRequiresChallenge);
 
         Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
-        mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
+        mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
         mBinder = new Binder();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 0d520ca..d4609b5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.CoexCoordinator;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -79,10 +80,13 @@
         when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
 
         mScheduler = new UserAwareBiometricScheduler(TAG,
+                new Handler(mLooper.getLooper()),
                 BiometricScheduler.SENSOR_TYPE_FP_OTHER,
                 null /* gestureAvailabilityDispatcher */,
+                mBiometricService,
                 () -> USER_ID,
-                mUserSwitchCallback);
+                mUserSwitchCallback,
+                CoexCoordinator.getInstance());
         mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
                 TAG, mScheduler, SENSOR_ID,
                 USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index c7c0756..0a0f7d7 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -26,6 +26,7 @@
 import static org.testng.Assert.assertThrows;
 
 import android.Manifest;
+import android.companion.virtual.VirtualDeviceParams;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.graphics.Point;
@@ -70,6 +71,8 @@
     private InputController.NativeWrapper mNativeWrapperMock;
     @Mock
     private DisplayManagerInternal mDisplayManagerInternalMock;
+    @Mock
+    private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
 
     @Before
     public void setUp() {
@@ -84,7 +87,8 @@
         mInputController = new InputController(new Object(), mNativeWrapperMock);
         mDeviceImpl = new VirtualDeviceImpl(mContext,
                 /* association info */ null, new Binder(), /* uid */ 0, mInputController,
-                (int associationId) -> {});
+                (int associationId) -> {}, mPendingTrampolineCallback,
+                new VirtualDeviceParams.Builder().build());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
new file mode 100644
index 0000000..77f1e24
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.companion.virtual.VirtualDeviceParams;
+import android.os.Parcel;
+import android.os.UserHandle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualDeviceParamsTest {
+
+    @Test
+    public void parcelable_shouldRecreateSuccessfully() {
+        VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder()
+                .setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED)
+                .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456)))
+                .build();
+        Parcel parcel = Parcel.obtain();
+        originalParams.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        VirtualDeviceParams params = VirtualDeviceParams.CREATOR.createFromParcel(parcel);
+        assertThat(params).isEqualTo(originalParams);
+        assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED);
+        assertThat(params.getUsersWithMatchingAccounts())
+                .containsExactly(UserHandle.of(123), UserHandle.of(456));
+    }
+}
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 3c809f9..1228d62 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -37,6 +37,8 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
 import static android.net.InetAddresses.parseNumericAddress;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -100,7 +102,7 @@
 import android.content.pm.UserInfo;
 import android.graphics.Color;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
+import android.net.ProfileNetworkPreference;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
@@ -4058,12 +4060,15 @@
         mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         dpms.handleStartUser(managedProfileUserId);
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                anyInt(),
-                any(),
-                any()
-        );
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                null, null);
     }
 
     @Test
@@ -4075,12 +4080,15 @@
         mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         dpms.handleStopUser(managedProfileUserId);
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
-                any(),
-                any()
-        );
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
     }
 
     @Test
@@ -4098,21 +4106,29 @@
 
         dpm.setPreferentialNetworkServiceEnabled(false);
         assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
-                any(),
-                any()
-        );
+
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
 
         dpm.setPreferentialNetworkServiceEnabled(true);
         assertThat(dpm.isPreferentialNetworkServiceEnabled()).isTrue();
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE),
-                any(),
-                any()
-        );
+
+        ProfileNetworkPreference preferenceDetails2 =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+                        .build();
+        List<ProfileNetworkPreference> preferences2 = new ArrayList<>();
+        preferences2.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences2,
+                        null, null);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
index e286cb2..d54524e 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
@@ -41,10 +41,10 @@
     @Test
     public void testConstruct() {
         final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
-                "CLOSED" /* name */, DeviceState.FLAG_CANCEL_STICKY_REQUESTS /* flags */);
+                "TEST_CLOSED" /* name */, DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS /* flags */);
         assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
-        assertEquals(state.getName(), "CLOSED");
-        assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS);
+        assertEquals(state.getName(), "TEST_CLOSED");
+        assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index c9cf2f0..b94fc43 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -213,6 +213,25 @@
         assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
     }
 
+    @Test
+    public void cancelOverrideRequestsTest() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                1 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                2 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.cancelOverrideRequests();
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+    }
+
     private static final class TestStatusChangeListener implements
             OverrideRequestController.StatusChangeListener {
         private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>();
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index abe7d89..54945e4 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyFloat;
@@ -32,18 +35,22 @@
 import android.hardware.SensorManager;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
+import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.testutils.OffsettableClock;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
@@ -59,7 +66,11 @@
     private static final float DOZE_SCALE_FACTOR = 0.0f;
     private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
-
+    private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000;
+    private static final int AMBIENT_LIGHT_HORIZON_LONG = 2000;
+    private static final float EPSILON = 0.001f;
+    private OffsettableClock mClock = new OffsettableClock();
+    private TestLooper mTestLooper;
     private Context mContext;
     private AutomaticBrightnessController mController;
 
@@ -89,21 +100,36 @@
         }
     }
 
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
     private AutomaticBrightnessController setupController(Sensor lightSensor) {
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+
         AutomaticBrightnessController controller = new AutomaticBrightnessController(
                 new AutomaticBrightnessController.Injector() {
                     @Override
                     public Handler getBackgroundThreadHandler() {
                         return mNoOpHandler;
                     }
-                },
-                () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
+
+                    @Override
+                    AutomaticBrightnessController.Clock createClock() {
+                        return mClock::now;
+                    }
+
+                }, // pass in test looper instead, pass in offsetable clock
+                () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
                 mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
-                mContext, mHbmController, mIdleBrightnessMappingStrategy
+                mContext, mHbmController, mIdleBrightnessMappingStrategy,
+                AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
         );
 
         when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
@@ -111,7 +137,7 @@
 
         // Configure the brightness controller and grab an instance of the sensor listener,
         // through which we can deliver fake (for test) sensor values.
-        controller.configure(true /* enable */, null /* configuration */,
+        controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
@@ -227,7 +253,7 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
 
         // User sets brightness to 100
-        mController.configure(true /* enable */, null /* configuration */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
@@ -250,7 +276,7 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
 
         // User sets brightness to 100
-        mController.configure(true /* enable */, null /* configuration */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
@@ -267,11 +293,102 @@
         verifyNoMoreInteractions(mBrightnessMappingStrategy);
 
         // User sets idle brightness to 0.5
-        mController.configure(true /* enable */, null /* configuration */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
         // Ensure we use the correct mapping strategy
         verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f);
     }
+
+    @Test
+    public void testAmbientLightHorizon() throws Exception {
+        // create abc
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        mController = setupController(lightSensor);
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        long increment = 500;
+        // set autobrightness to low
+        // t = 0
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+
+        // t = 500
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+
+        // t = 1000
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 1500
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 2000
+        // ensure that our reading is at 0.
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 2500
+        // first 10000 lux sensor event reading
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 3000
+        // lux reading should still not yet be 10000.
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 3500
+        mClock.fastForward(increment);
+        // lux has been high (10000) for 1000ms.
+        // lux reading should be 10000
+        // short horizon (ambient lux) is high, long horizon is still not high
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 4000
+        // stay high
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 4500
+        Mockito.clearInvocations(mBrightnessMappingStrategy);
+        mClock.fastForward(increment);
+        // short horizon is high, long horizon is high too
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 10000));
+        verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1);
+        assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
+
+        // t = 5000
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 5500
+        mClock.fastForward(increment);
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertTrue(mController.getAmbientLux() > 0.0f);
+        assertTrue(mController.getAmbientLux() < 10000.0f);
+
+        // t = 6000
+        mClock.fastForward(increment);
+        // ambient lux goes to 0
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 0));
+        assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index aca8632..beecdad 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -20,6 +20,11 @@
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
 
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+import static com.android.server.display.AutomaticBrightnessController
+                                                      .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
+
 import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
 
 import static org.junit.Assert.assertEquals;
@@ -47,6 +52,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
@@ -87,6 +93,7 @@
     private TestLooper mTestLooper;
     private Handler mHandler;
     private Binder mDisplayToken;
+    private String mDisplayUniqueId;
     private Context mContextSpy;
 
     @Rule
@@ -108,6 +115,7 @@
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
         mDisplayToken = null;
+        mDisplayUniqueId = "unique_id";
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -123,8 +131,8 @@
     public void testNoHbmData() {
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
-                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
-                DEFAULT_MAX, null, () -> {}, mContextSpy);
+                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
         assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
     }
@@ -133,9 +141,9 @@
     public void testNoHbmData_Enabled() {
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
-                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
-                DEFAULT_MAX, null, () -> {}, mContextSpy);
-        hbmc.setAutoBrightnessEnabled(true);
+                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
         assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
@@ -152,7 +160,7 @@
     public void testAutoBrightnessEnabled_NoLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
     }
 
@@ -160,7 +168,7 @@
     public void testAutoBrightnessEnabled_LowLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
     }
@@ -169,7 +177,7 @@
     public void testAutoBrightnessEnabled_HighLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
     }
@@ -178,9 +186,9 @@
     public void testAutoBrightnessEnabled_HighLux_ThenDisable() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
-        hbmc.setAutoBrightnessEnabled(false);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_DISABLED);
 
         assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);
     }
@@ -189,7 +197,7 @@
     public void testWithinHighRange_thenOverTime_thenEarnBackTime() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
 
@@ -221,7 +229,7 @@
     public void testInHBM_ThenLowLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
 
@@ -245,7 +253,7 @@
     public void testInHBM_TestMultipleEvents_DueToAutoBrightness() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
 
         hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
@@ -274,7 +282,7 @@
     public void testInHBM_TestMultipleEvents_DueToLux() {
         final HighBrightnessModeController hbmc = createDefaultHbm();
 
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
 
         // Go into HBM for half the allowed window
@@ -316,7 +324,7 @@
         listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
 
         // Try to go into HBM mode but fail
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         advanceTime(10);
 
@@ -335,7 +343,7 @@
         listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT));
 
         // Try to go into HBM mode
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         advanceTime(1);
 
@@ -378,7 +386,7 @@
         final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
 
         // Turn on sunlight
-        hbmc.setAutoBrightnessEnabled(true);
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
         advanceTime(0);
         assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
@@ -451,6 +459,137 @@
         assertEquals(expectedHdrBrightness, hbmc.getHdrBrightnessValue(), EPSILON);
     }
 
+    @Test
+    public void testHbmStats_StateChange() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onBrightnessChanged(TRANSITION_POINT);
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+        advanceTime(0);
+        assertEquals(HIGH_BRIGHTNESS_MODE_HDR, hbmc.getHighBrightnessMode());
+
+        // Verify Stats HBM_ON_HDR
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 0 /*numberOfHdrLayers*/,
+                0, 0, 0 /*flags*/);
+        advanceTime(0);
+
+        // Verify Stats HBM_OFF
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+
+        // Verify Stats HBM_ON_SUNLIGHT
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.onAmbientLuxChange(1);
+        advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1);
+
+        // Verify Stats HBM_OFF
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
+    }
+
+    @Test
+    public void testHbmStats_ThermalOff() throws Exception {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        verify(mThermalServiceMock).registerThermalEventListenerWithType(
+                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+        final IThermalEventListener thermListener = mThermalEventListenerCaptor.getValue();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(1);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        thermListener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+        advanceTime(10);
+        assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+    }
+
+    @Test
+    public void testHbmStats_TimeOut() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(0);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        // Use up all the time in the window.
+        advanceTime(TIME_WINDOW_MILLIS + 1);
+
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
+    }
+
+    @Test
+    public void testHbmStats_DisplayOff() {
+        final HighBrightnessModeController hbmc = createDefaultHbm();
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(0);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
+    }
+
+    @Test
+    public void testHbmStats_HdrPlaying() {
+        final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
+        final int displayStatsId = mDisplayUniqueId.hashCode();
+
+        hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
+        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
+        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
+        advanceTime(0);
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+
+        hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/);
+        advanceTime(0);
+
+        verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+            eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
+    }
+
     private void assertState(HighBrightnessModeController hbmc,
             float brightnessMin, float brightnessMax, int hbmMode) {
         assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON);
@@ -466,8 +605,8 @@
     private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
         initHandler(clock);
         return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
-                DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {},
-                mContextSpy);
+                DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
+                DEFAULT_HBM_DATA, () -> {}, mContextSpy);
     }
 
     private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index b588db6..18f2642 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -92,7 +92,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         mHdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index ff01cb1..e4c5ad67 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -109,7 +109,6 @@
                 hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
         hdmiControlService.setCecController(hdmiCecController);
         hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
-        hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
         hdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         hdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index a44a5cd..d73cdb5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -102,7 +102,6 @@
                 hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
         hdmiControlService.setCecController(hdmiCecController);
         hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
-        hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
         hdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         hdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 9c99240..5cec8ad 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -53,7 +53,7 @@
 
     @Before
     public void SetUp() {
-        mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+        mDeviceInfoForTests = HdmiDeviceInfo.hardwarePort(1001, 1234);
         HdmiControlService hdmiControlService =
                 new HdmiControlService(InstrumentationRegistry.getTargetContext(),
                         Collections.emptyList()) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 638b386..52a0b6c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -109,7 +109,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         mHdmiControlService.setPowerManager(mPowerManager);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index 41231e0..35432ed 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -128,10 +128,8 @@
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
         mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
                 mHdmiCecController, mHdmiMhlControllerStub);
         mHdmiControlService.setHdmiCecNetwork(mHdmiCecNetwork);
@@ -156,13 +154,13 @@
         mPlaybackLogicalAddress3 = mPlaybackLogicalAddress1 == ADDR_PLAYBACK_3
                 ? ADDR_PLAYBACK_1 : ADDR_PLAYBACK_3;
 
-        mReportPowerStatusOn = new HdmiCecMessage(
+        mReportPowerStatusOn = HdmiCecMessage.build(
                 mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
                 Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
-        mReportPowerStatusStandby = new HdmiCecMessage(
+        mReportPowerStatusStandby = HdmiCecMessage.build(
                 mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
                 Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
-        mReportPowerStatusTransientToOn = new HdmiCecMessage(
+        mReportPowerStatusTransientToOn = HdmiCecMessage.build(
                 mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
                 Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON);
         mSetStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
@@ -173,21 +171,36 @@
         mActiveSource = HdmiCecMessageBuilder.buildActiveSource(
                 mPlaybackLogicalAddress2, PHYSICAL_ADDRESS_PLAYBACK_2);
 
-        HdmiDeviceInfo infoPlayback1 = new HdmiDeviceInfo(
-                mPlaybackLogicalAddress1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1,
-                HdmiDeviceInfo.DEVICE_PLAYBACK,
-                0x1234, "Playback 1",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        HdmiDeviceInfo infoPlayback2 = new HdmiDeviceInfo(
-                mPlaybackLogicalAddress2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2,
-                HdmiDeviceInfo.DEVICE_PLAYBACK,
-                0x1234, "Playback 2",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-        HdmiDeviceInfo infoPlayback3 = new HdmiDeviceInfo(
-                mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3, PORT_3,
-                HdmiDeviceInfo.DEVICE_PLAYBACK,
-                0x1234, "Playback 3",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+        HdmiDeviceInfo infoPlayback1 = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mPlaybackLogicalAddress1)
+                .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_1)
+                .setPortId(PORT_1)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 1")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
+        HdmiDeviceInfo infoPlayback2 = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mPlaybackLogicalAddress2)
+                .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_2)
+                .setPortId(PORT_2)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 2")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
+        HdmiDeviceInfo infoPlayback3 = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(mPlaybackLogicalAddress3)
+                .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_3)
+                .setPortId(PORT_3)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 3")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
 
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback1);
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback2);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index dd74864..e77cd91 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -64,22 +64,34 @@
     private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON };
     private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY };
     private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON };
-    private static final HdmiCecMessage REPORT_POWER_STATUS_ON = new HdmiCecMessage(
+    private static final HdmiCecMessage REPORT_POWER_STATUS_ON = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
-    private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage(
+    private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
-    private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage(
+    private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_TV, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON);
     private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath(
                         ADDR_TV, PHYSICAL_ADDRESS_PLAYBACK_1);
-    private static final HdmiDeviceInfo INFO_PLAYBACK_1 = new HdmiDeviceInfo(
-            ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1, HdmiDeviceInfo.DEVICE_PLAYBACK,
-            0x1234, "Playback 1",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
-    private static final HdmiDeviceInfo INFO_PLAYBACK_2 = new HdmiDeviceInfo(
-            ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2, HdmiDeviceInfo.DEVICE_PLAYBACK,
-            0x1234, "Playback 2",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+    private static final HdmiDeviceInfo INFO_PLAYBACK_1 = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_PLAYBACK_1)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_1)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+            .setVendorId(0x1234)
+            .setDisplayName("Plyback 1")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
+    private static final HdmiDeviceInfo INFO_PLAYBACK_2 = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_PLAYBACK_2)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYBACK_2)
+            .setPortId(PORT_2)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+            .setVendorId(0x1234)
+            .setDisplayName("Playback 2")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
 
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
@@ -123,7 +135,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index d630ef6..559a2c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.Iterables;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -77,7 +78,8 @@
         if (body.length == 0) {
             return mPollAddressResponse[dstAddress];
         } else {
-            HdmiCecMessage message = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+            HdmiCecMessage message = HdmiCecMessage.build(srcAddress, dstAddress, body[0],
+                    Arrays.copyOfRange(body, 1, body.length));
             mResultMessages.add(message);
             return mMessageSendResult.getOrDefault(message.getOpcode(), SendMessageResult.SUCCESS);
         }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java
new file mode 100644
index 0000000..0b31db6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_VERSION_2_0;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class GiveFeaturesActionTest {
+    private HdmiControlService mHdmiControlServiceSpy;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDevicePlayback mPlaybackDevice;
+    private FakeNativeWrapper mNativeWrapper;
+    private FakePowerManagerWrapper mPowerManager;
+    private Looper mLooper;
+    private Context mContextSpy;
+    private TestLooper mTestLooper = new TestLooper();
+    private int mPhysicalAddress = 0x1100;
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mPlaybackLogicalAddress;
+
+    private TestCallback mTestCallback;
+    private GiveFeaturesAction mAction;
+
+    /**
+     * Setup: Local Playback device queries the features of a connected TV.
+     */
+    @Before
+    public void setUp() throws RemoteException {
+        mContextSpy = spy(new ContextWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext()));
+
+        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
+        doNothing().when(mHdmiControlServiceSpy)
+                .writeStringSystemProperty(anyString(), anyString());
+
+        mLooper = mTestLooper.getLooper();
+        mHdmiControlServiceSpy.setIoLooper(mLooper);
+        mHdmiControlServiceSpy.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+
+        mNativeWrapper = new FakeNativeWrapper();
+        mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+
+        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlServiceSpy, mNativeWrapper, mHdmiControlServiceSpy.getAtomWriter());
+        mHdmiControlServiceSpy.setCecController(mHdmiCecController);
+        mHdmiControlServiceSpy.setHdmiMhlController(
+                HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
+        mHdmiControlServiceSpy.initService();
+        mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+        mHdmiControlServiceSpy.setPowerManager(mPowerManager);
+
+        mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy);
+        mPlaybackDevice.init();
+        mLocalDevices.add(mPlaybackDevice);
+
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        mTestLooper.dispatchAll();
+
+        synchronized (mPlaybackDevice.mLock) {
+            mPlaybackLogicalAddress = mPlaybackDevice.getDeviceInfo().getLogicalAddress();
+        }
+
+        // Setup specific to these tests
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                Constants.ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV));
+        mTestLooper.dispatchAll();
+
+        mTestCallback = new TestCallback();
+        mAction = new GiveFeaturesAction(mPlaybackDevice, Constants.ADDR_TV, mTestCallback);
+    }
+
+    @Test
+    public void sendsGiveFeaturesMessage() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveFeatures = HdmiCecMessageBuilder.buildGiveFeatures(
+                mPlaybackLogicalAddress, Constants.ADDR_TV);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveFeatures);
+    }
+
+    @Test
+    public void noMatchingReportFeaturesReceived_actionFailsAndNetworkIsNotUpdated() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        // Wrong source
+        mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
+                Constants.ADDR_AUDIO_SYSTEM, HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Arrays.asList(DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_SOURCE,
+                Collections.emptyList(), DeviceFeatures.NO_FEATURES_SUPPORTED));
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
+        assertThat(mTestCallback.getResult()).isEqualTo(
+                HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+    }
+
+    @Test
+    public void matchingReportFeaturesReceived_actionSucceedsAndNetworkIsUpdated() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.onCecMessage(
+                ReportFeaturesMessage.build(
+                        Constants.ADDR_TV, HDMI_CEC_VERSION_2_0, Collections.emptyList(),
+                        Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                        DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)
+                                .build()
+                )
+        );
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    private static class TestCallback extends IHdmiControlCallback.Stub {
+        private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+        @Override
+        public void onComplete(int result) {
+            mCallbackResult.add(result);
+        }
+
+        private int getResult() {
+            assertThat(mCallbackResult.size()).isEqualTo(1);
+            return mCallbackResult.get(0);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index f30e97a..30bcc7e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -98,8 +98,6 @@
         doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
 
         mHdmiControlServiceSpy.setIoLooper(mLooper);
-        mHdmiControlServiceSpy.setMessageValidator(
-                new HdmiCecMessageValidator(mHdmiControlServiceSpy));
         mHdmiControlServiceSpy.setCecMessageBuffer(
                 new CecMessageBuffer(mHdmiControlServiceSpy));
 
@@ -226,7 +224,7 @@
 
     @Test
     public void testMessageReported_writesAtom_userControlPressed_noParams() {
-        HdmiCecMessage message = new HdmiCecMessage(
+        HdmiCecMessage message = HdmiCecMessage.build(
                 Constants.ADDR_TV,
                 Constants.ADDR_PLAYBACK_1,
                 Constants.MESSAGE_USER_CONTROL_PRESSED,
@@ -279,7 +277,7 @@
 
     @Test
     public void testMessageReported_writesAtom_featureAbort_noParams() {
-        HdmiCecMessage message = new HdmiCecMessage(
+        HdmiCecMessage message = HdmiCecMessage.build(
                 Constants.ADDR_TV,
                 Constants.ADDR_PLAYBACK_1,
                 Constants.MESSAGE_FEATURE_ABORT,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index a411392..70bc460 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -188,7 +188,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
         mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 2d13e69..6fc3354 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -59,10 +59,16 @@
             HotplugDetectionAction.POLLING_INTERVAL_MS_FOR_PLAYBACK;
 
     private static final int PORT_1 = 1;
-    private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
-            ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
-            0x1234, "TV",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+    private static final HdmiDeviceInfo INFO_TV = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_TV)
+            .setPhysicalAddress(0x0000)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_TV)
+            .setVendorId(0x1234)
+            .setDisplayName("TV")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
 
     private HdmiControlService mHdmiControlService;
     private HdmiCecController mHdmiCecController;
@@ -138,7 +144,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
@@ -1691,10 +1696,16 @@
     public void hotplugDetectionAction_removeDevice() {
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
-        HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo(
-                Constants.ADDR_PLAYBACK_2, 0x1234, PORT_1,
-                HdmiDeviceInfo.DEVICE_PLAYBACK, 0x1234, "Playback 2",
-                HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+        HdmiDeviceInfo infoPlayback = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_2)
+                .setPhysicalAddress(0x1234)
+                .setPortId(PORT_1)
+                .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+                .setVendorId(0x1234)
+                .setDisplayName("Playback 2")
+                .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+                .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+                .build();
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback);
         // This logical address (ADDR_PLAYBACK_2) won't acknowledge the poll message sent by the
         // HotplugDetectionAction so it shall be removed.
@@ -1726,8 +1737,15 @@
 
     @Test
     public void getActiveSource_deviceInNetworkIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x3000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x3000)
+                .setPortId(0)
+                .setDeviceType(Constants.ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
+
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(externalDevice);
         mTestLooper.dispatchAll();
 
@@ -1739,8 +1757,14 @@
 
     @Test
     public void getActiveSource_unknownDeviceIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x3000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x3000)
+                .setPortId(0)
+                .setDeviceType(Constants.ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
 
         mHdmiControlService.setActiveSource(externalDevice.getLogicalAddress(),
                 externalDevice.getPhysicalAddress(), "HdmiControlServiceTest");
@@ -1933,7 +1957,7 @@
 
     @Test
     public void doesNotSupportRecordTvScreen() {
-        HdmiCecMessage recordTvScreen = new HdmiCecMessage(ADDR_TV, mPlaybackLogicalAddress,
+        HdmiCecMessage recordTvScreen = HdmiCecMessage.build(ADDR_TV, mPlaybackLogicalAddress,
                 Constants.MESSAGE_RECORD_TV_SCREEN, HdmiCecMessage.EMPTY_PARAM);
 
         mNativeWrapper.onCecMessage(recordTvScreen);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index f5af6df..fb8baa3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -109,11 +109,6 @@
         protected List<Integer> getRcFeatures() {
             return Collections.emptyList();
         }
-
-        @Override
-        protected List<Integer> getDeviceFeatures() {
-            return Collections.emptyList();
-        }
     }
 
     private MyHdmiCecLocalDevice mHdmiLocalDevice;
@@ -187,14 +182,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
-        mMessageValidator =
-                new HdmiCecMessageValidator(mHdmiControlService) {
-                    @Override
-                    int isValid(HdmiCecMessage message, boolean isMessageReceived) {
-                        return HdmiCecMessageValidator.OK;
-                    }
-                };
-        mHdmiControlService.setMessageValidator(mMessageValidator);
 
         mLocalDevices.add(mHdmiLocalDevice);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
@@ -230,7 +217,7 @@
     @Test
     public void dispatchMessage_logicalAddressDoesNotMatch() {
         HdmiCecMessage msg =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         ADDR_PLAYBACK_1,
                         Constants.MESSAGE_CEC_VERSION,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index a260a6d..b6c4bc2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -121,7 +121,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
@@ -172,8 +171,14 @@
 
     @Test
     public void getActiveSource_deviceInNetworkIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x1000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x3000)
+                .setPortId(0)
+                .setDeviceType(Constants.ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(externalDevice);
         mTestLooper.dispatchAll();
 
@@ -185,7 +190,7 @@
 
     @Test
     public void getActiveSource_unknownLogicalAddressInNetworkIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(0x1000, 1);
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.hardwarePort(0x1000, 1);
 
         mHdmiControlService.setActiveSource(Constants.ADDR_UNREGISTERED,
                 externalDevice.getPhysicalAddress(), "HdmiControlServiceTest");
@@ -197,8 +202,14 @@
 
     @Test
     public void getActiveSource_unknownDeviceIsActiveSource() {
-        HdmiDeviceInfo externalDevice = new HdmiDeviceInfo(Constants.ADDR_PLAYBACK_3, 0x1000, 0,
-                Constants.ADDR_PLAYBACK_1, 0, "Test Device");
+        HdmiDeviceInfo externalDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(Constants.ADDR_PLAYBACK_3)
+                .setPhysicalAddress(0x0000)
+                .setPortId(0)
+                .setDeviceType(ADDR_PLAYBACK_1)
+                .setVendorId(0)
+                .setDisplayName("Test Device")
+                .build();
 
         mHdmiControlService.setActiveSource(externalDevice.getLogicalAddress(),
                 externalDevice.getPhysicalAddress(), "HdmiControlServiceTest");
@@ -240,7 +251,7 @@
                 HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
         mTestLooper.dispatchAll();
         mPowerManager.setInteractive(false);
-        HdmiCecMessage imageViewOn = new HdmiCecMessage(ADDR_PLAYBACK_1, mTvLogicalAddress,
+        HdmiCecMessage imageViewOn = HdmiCecMessage.build(ADDR_PLAYBACK_1, mTvLogicalAddress,
                 Constants.MESSAGE_IMAGE_VIEW_ON, HdmiCecMessage.EMPTY_PARAM);
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(imageViewOn)).isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
@@ -268,7 +279,7 @@
                 HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED);
         mTestLooper.dispatchAll();
         mPowerManager.setInteractive(false);
-        HdmiCecMessage imageViewOn = new HdmiCecMessage(ADDR_PLAYBACK_1, mTvLogicalAddress,
+        HdmiCecMessage imageViewOn = HdmiCecMessage.build(ADDR_PLAYBACK_1, mTvLogicalAddress,
                 Constants.MESSAGE_IMAGE_VIEW_ON, HdmiCecMessage.EMPTY_PARAM);
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(imageViewOn)).isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
@@ -478,7 +489,7 @@
 
     @Test
     public void supportsRecordTvScreen() {
-        HdmiCecMessage recordTvScreen = new HdmiCecMessage(ADDR_RECORDER_1, mTvLogicalAddress,
+        HdmiCecMessage recordTvScreen = HdmiCecMessage.build(ADDR_RECORDER_1, mTvLogicalAddress,
                 Constants.MESSAGE_RECORD_TV_SCREEN, HdmiCecMessage.EMPTY_PARAM);
 
         mNativeWrapper.onCecMessage(recordTvScreen);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 453303e..f869462 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -22,20 +22,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
-import com.google.android.collect.Lists;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.Collections;
-
 @SmallTest
 @Presubmit
 @RunWith(JUnit4.class)
@@ -100,89 +95,4 @@
 
         assertThat(message).isEqualTo(buildMessage("40:A5"));
     }
-
-    @Test
-    public void buildReportFeatures_basicTv_1_4() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:05:80:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_basicPlayback_1_4() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("4F:A6:05:10:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_basicPlaybackAudioSystem_1_4() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
-                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
-                        HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("4F:A6:05:18:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_basicTv_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_remoteControlTv_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:02:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_remoteControlPlayback_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
-                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
-                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList());
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:00"));
-    }
-
-    @Test
-    public void buildReportFeatures_deviceFeaturesTv_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
-                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
-                Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN));
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:40"));
-    }
-
-    @Test
-    public void buildReportFeatures_deviceFeaturesPlayback_2_0() {
-        HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
-                HdmiControlManager.HDMI_CEC_VERSION_2_0,
-                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
-                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
-                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
-                Lists.newArrayList(Constants.DEVICE_FEATURE_SUPPORTS_DECK_CONTROL));
-
-        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:10"));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
index cca5094..2984cfa 100755
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
@@ -41,12 +41,12 @@
 
         new EqualsTester()
                 .addEqualityGroup(
-                        new HdmiCecMessage(source, destination, opcode, params1),
-                        new HdmiCecMessage(source, destination, opcode, params1))
-                .addEqualityGroup(new HdmiCecMessage(source, destination, opcode, params2))
-                .addEqualityGroup(new HdmiCecMessage(source + 1, destination, opcode, params1))
-                .addEqualityGroup(new HdmiCecMessage(source, destination + 1, opcode, params1))
-                .addEqualityGroup(new HdmiCecMessage(source, destination, opcode + 1, params1))
+                        HdmiCecMessage.build(source, destination, opcode, params1),
+                        HdmiCecMessage.build(source, destination, opcode, params1))
+                .addEqualityGroup(HdmiCecMessage.build(source, destination, opcode, params2))
+                .addEqualityGroup(HdmiCecMessage.build(source + 1, destination, opcode, params1))
+                .addEqualityGroup(HdmiCecMessage.build(source, destination + 1, opcode, params1))
+                .addEqualityGroup(HdmiCecMessage.build(source, destination, opcode + 1, params1))
                 .testEquals();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 548a439..50c9f70 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -54,7 +54,6 @@
                 InstrumentationRegistry.getTargetContext(), Collections.emptyList());
 
         mHdmiControlService.setIoLooper(mTestLooper.getLooper());
-        mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
     }
 
     @Test
@@ -400,16 +399,6 @@
     }
 
     @Test
-    public void isValid_reportFeatures() {
-        assertMessageValidity("0F:A6:05:80:00:00").isEqualTo(OK);
-
-        assertMessageValidity("04:A6:05:80:00:00").isEqualTo(ERROR_DESTINATION);
-        assertMessageValidity("FF:A6:05:80:00:00").isEqualTo(ERROR_SOURCE);
-
-        assertMessageValidity("0F:A6").isEqualTo(ERROR_PARAMETER_SHORT);
-    }
-
-    @Test
     public void isValid_deckControl() {
         assertMessageValidity("40:42:01:6E").isEqualTo(OK);
         assertMessageValidity("40:42:04").isEqualTo(OK);
@@ -649,6 +638,6 @@
     }
 
     private IntegerSubject assertMessageValidity(String message) {
-        return assertThat(mHdmiCecMessageValidator.isValid(HdmiUtils.buildMessage(message), false));
+        return assertThat(HdmiUtils.buildMessage(message).getValidationResult());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index 1048eb5..42fa32c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -17,9 +17,12 @@
 package com.android.server.hdmi;
 
 
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.hardware.hdmi.DeviceFeatures;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -80,7 +83,6 @@
         mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
 
         mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
                 mHdmiCecController, mHdmiMhlControllerStub);
@@ -178,7 +180,7 @@
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
                 HdmiControlManager.POWER_STATUS_UNKNOWN);
 
@@ -216,7 +218,7 @@
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
                 HdmiControlManager.POWER_STATUS_UNKNOWN);
     }
@@ -258,7 +260,7 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
                 Constants.INVALID_PHYSICAL_ADDRESS);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
@@ -279,7 +281,7 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
                 Constants.INVALID_PHYSICAL_ADDRESS);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
                 HdmiControlManager.POWER_STATUS_UNKNOWN);
@@ -471,7 +473,7 @@
         assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
                 Constants.INVALID_PHYSICAL_ADDRESS);
         assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
-        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+        assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.VENDOR_ID_UNKNOWN);
         assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
                 HdmiUtils.getDefaultDeviceName(logicalAddress));
         assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
@@ -514,12 +516,14 @@
         int logicalAddress = Constants.ADDR_PLAYBACK_1;
         int cecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
         mHdmiCecNetwork.handleCecMessage(
-                HdmiCecMessageBuilder.buildReportFeatures(logicalAddress,
+                ReportFeaturesMessage.build(logicalAddress,
                         cecVersion, Collections.emptyList(),
                         Constants.RC_PROFILE_SOURCE, Collections.emptyList(),
-                        Collections.emptyList()));
+                        DeviceFeatures.NO_FEATURES_SUPPORTED));
 
-        assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        synchronized (mHdmiCecNetwork.mLock) {
+            assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        }
 
         HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
         assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
@@ -531,12 +535,14 @@
         int logicalAddress = Constants.ADDR_PLAYBACK_1;
         int cecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
         mHdmiCecNetwork.handleCecMessage(
-                HdmiCecMessageBuilder.buildReportFeatures(logicalAddress,
+                ReportFeaturesMessage.build(logicalAddress,
                         cecVersion, Collections.emptyList(),
                         Constants.RC_PROFILE_SOURCE, Collections.emptyList(),
-                        Collections.emptyList()));
+                        DeviceFeatures.NO_FEATURES_SUPPORTED));
 
-        assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        synchronized (mHdmiCecNetwork.mLock) {
+            assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        }
 
         HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
         assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
@@ -544,10 +550,33 @@
     }
 
     @Test
-    public void getSafeCecDevicesLocked_addDevice_sizeOne() {
-        HdmiDeviceInfo cecDeviceInfo = new HdmiDeviceInfo();
+    public void cecDevices_tracking_reportFeatures_updatesDeviceFeatures() {
+        // Features should be set correctly with the initial <Report Features>
+        int logicalAddress = Constants.ADDR_PLAYBACK_1;
+        int cecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
+        DeviceFeatures deviceFeatures = DeviceFeatures.NO_FEATURES_SUPPORTED;
+        mHdmiCecNetwork.handleCecMessage(
+                ReportFeaturesMessage.build(logicalAddress,
+                        cecVersion, Collections.emptyList(),
+                        Constants.RC_PROFILE_SOURCE, Collections.emptyList(), deviceFeatures));
 
-        mHdmiCecNetwork.addCecDevice(cecDeviceInfo);
+        HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+        assertThat(cecDeviceInfo.getDeviceFeatures()).isEqualTo(deviceFeatures);
+
+        // New information from <Report Features> should override old information
+        DeviceFeatures updatedFeatures = DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                .setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED).build();
+        mHdmiCecNetwork.handleCecMessage(
+                ReportFeaturesMessage.build(logicalAddress,
+                        cecVersion, Collections.emptyList(),
+                        Constants.RC_PROFILE_SOURCE, Collections.emptyList(), updatedFeatures));
+        cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+        assertThat(cecDeviceInfo.getDeviceFeatures()).isEqualTo(updatedFeatures);
+    }
+
+    @Test
+    public void getSafeCecDevicesLocked_addDevice_sizeOne() {
+        mHdmiCecNetwork.addCecDevice(HdmiDeviceInfo.INACTIVE_DEVICE);
 
         assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index bff1296..7a68285 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -98,7 +98,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index a44bd8e..7751ef5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -111,8 +111,6 @@
         mHdmiControlServiceSpy.setCecController(mHdmiCecController);
         mHdmiControlServiceSpy.setHdmiMhlController(HdmiMhlControllerStub.create(
                 mHdmiControlServiceSpy));
-        mHdmiControlServiceSpy.setMessageValidator(new HdmiCecMessageValidator(
-                mHdmiControlServiceSpy));
 
         mLocalDevices.add(mAudioSystemDeviceSpy);
         mLocalDevices.add(mPlaybackDeviceSpy);
@@ -487,7 +485,7 @@
                 Constants.ADDR_PLAYBACK_1));
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
                 Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
@@ -505,7 +503,7 @@
         mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
                 Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
@@ -522,7 +520,7 @@
         mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
+        HdmiCecMessage reportFeatures = ReportFeaturesMessage.build(
                 Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
                 Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
                 mPlaybackDeviceSpy.getRcProfile(), mPlaybackDeviceSpy.getRcFeatures(),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 22ad956..561e6a5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -57,10 +57,16 @@
             new byte[]{HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON};
 
     private static final int PORT_1 = 1;
-    private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
-            ADDR_TV, 0x0000, PORT_1, HdmiDeviceInfo.DEVICE_TV,
-            0x1234, "TV",
-            HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+    private static final HdmiDeviceInfo INFO_TV = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_TV)
+            .setPhysicalAddress(0x0000)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_TV)
+            .setVendorId(0x1234)
+            .setDisplayName("TV")
+            .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON)
+            .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B)
+            .build();
 
     private Context mContextSpy;
     private HdmiControlService mHdmiControlService;
@@ -113,7 +119,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
         mHdmiControlService.setPowerManager(mPowerManager);
@@ -165,7 +170,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -218,7 +223,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -271,7 +276,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusTransientToOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -284,7 +289,7 @@
         mNativeWrapper.clearResultMessages();
 
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -428,7 +433,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
@@ -482,7 +487,7 @@
         mNativeWrapper.clearResultMessages();
         assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
         HdmiCecMessage reportPowerStatusOn =
-                new HdmiCecMessage(
+                HdmiCecMessage.build(
                         ADDR_TV,
                         playbackDevice.getDeviceInfo().getLogicalAddress(),
                         Constants.MESSAGE_REPORT_POWER_STATUS,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 2f22bce..c878f99 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -99,7 +99,6 @@
                 this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mTvDevice = new HdmiCecLocalDeviceTv(mHdmiControlService);
         mTvDevice.init();
         mLocalDevices.add(mTvDevice);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ReportFeaturesMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ReportFeaturesMessageTest.java
new file mode 100644
index 0000000..22f1f43
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ReportFeaturesMessageTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
+import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
+import static com.android.server.hdmi.HdmiUtils.buildMessage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ReportFeaturesMessageTest {
+    @Test
+    public void build_invalidMessages() {
+        assertThat(HdmiUtils.buildMessage("FF:A6:05:80:00:00")
+                .getValidationResult()).isEqualTo(ERROR_SOURCE);
+        assertThat(HdmiUtils.buildMessage("04:A6:05:80:00:00")
+                .getValidationResult()).isEqualTo(ERROR_DESTINATION);
+        assertThat(HdmiUtils.buildMessage("0F:A6")
+                .getValidationResult()).isEqualTo(ERROR_PARAMETER_SHORT);
+        assertThat(HdmiUtils.buildMessage("4F:A6:06:00:80:80:00")
+                .getValidationResult()).isEqualTo(ERROR_PARAMETER_SHORT);
+    }
+
+    @Test
+    public void build_longMessage() {
+        HdmiCecMessage longMessage = HdmiUtils.buildMessage("4F:A6:05:00:80:80:00:81:80:00");
+        assertThat(longMessage).isInstanceOf(ReportFeaturesMessage.class);
+        ReportFeaturesMessage longReportFeaturesMessage = (ReportFeaturesMessage) longMessage;
+
+        HdmiCecMessage shortMessage = HdmiUtils.buildMessage("4F:A6:05:00:00:01");
+        assertThat(shortMessage).isInstanceOf(ReportFeaturesMessage.class);
+        ReportFeaturesMessage shortReportFeaturesMessage = (ReportFeaturesMessage) shortMessage;
+
+        assertThat(longReportFeaturesMessage.getDeviceFeatures()).isEqualTo(
+                shortReportFeaturesMessage.getDeviceFeatures());
+        assertThat(longReportFeaturesMessage.getCecVersion()).isEqualTo(
+                shortReportFeaturesMessage.getCecVersion());
+    }
+
+    @Test
+    public void build_basicTv_1_4() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:05:80:00:00"));
+    }
+
+    @Test
+    public void build_basicPlayback_1_4() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_PLAYBACK_1,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("4F:A6:05:10:00:00"));
+    }
+
+    @Test
+    public void build_basicPlaybackAudioSystem_1_4() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_PLAYBACK_1,
+                HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
+                        HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("4F:A6:05:18:00:00"));
+    }
+
+    @Test
+    public void build_basicTv_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:00"));
+    }
+
+    @Test
+    public void build_remoteControlTv_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_ONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:02:00"));
+    }
+
+    @Test
+    public void build_remoteControlPlayback_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
+                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
+                DeviceFeatures.NO_FEATURES_SUPPORTED);
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:00"));
+    }
+
+    @Test
+    public void build_deviceFeaturesTv_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
+                Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+                DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                        .setRecordTvScreenSupport(DeviceFeatures.FEATURE_SUPPORTED)
+                        .build());
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:40"));
+    }
+
+    @Test
+    public void build_deviceFeaturesPlayback_2_0() {
+        HdmiCecMessage message = ReportFeaturesMessage.build(ADDR_TV,
+                HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
+                Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                        Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
+                DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+                        .setDeckControlSupport(DeviceFeatures.FEATURE_SUPPORTED)
+                        .build());
+
+        assertThat(message.getValidationResult()).isEqualTo(OK);
+        assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:10"));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 2d81fc9..6184c21 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -123,7 +123,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         mHdmiControlService.initService();
         mPowerManager = new FakePowerManagerWrapper(context);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index 302c0ac..0587864 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -101,19 +101,29 @@
     private static final byte[] PLAYER_PARAM =
             new byte[]{(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF};
 
-    private static final HdmiDeviceInfo DEVICE_INFO_AVR =
-            new HdmiDeviceInfo(ADDR_AUDIO_SYSTEM, PHYSICAL_ADDRESS_AVR, PORT_1,
-                    HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, VENDOR_ID_AVR, "Audio");
-    private static final HdmiDeviceInfo DEVICE_INFO_PLAYER =
-            new HdmiDeviceInfo(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYER, PORT_1,
-                    HdmiDeviceInfo.DEVICE_PLAYBACK, VENDOR_ID_AVR, "Player");
-    private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = new HdmiCecMessage(
+    private static final HdmiDeviceInfo DEVICE_INFO_AVR = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_AUDIO_SYSTEM)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_AVR)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)
+            .setVendorId(VENDOR_ID_AVR)
+            .setDisplayName("Audio")
+            .build();
+    private static final HdmiDeviceInfo DEVICE_INFO_PLAYER = HdmiDeviceInfo.cecDeviceBuilder()
+            .setLogicalAddress(ADDR_PLAYBACK_1)
+            .setPhysicalAddress(PHYSICAL_ADDRESS_PLAYER)
+            .setPortId(PORT_1)
+            .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK)
+            .setVendorId(VENDOR_ID_AVR)
+            .setDisplayName("Player")
+            .build();
+    private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = HdmiCecMessage.build(
             ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM);
-    private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = new HdmiCecMessage(
+    private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = HdmiCecMessage.build(
             ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM);
-    private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = new HdmiCecMessage(
+    private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = HdmiCecMessage.build(
             ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM);
-    private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = new HdmiCecMessage(
+    private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = HdmiCecMessage.build(
             ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM);
 
     private HdmiControlService mHdmiControlService;
@@ -169,7 +179,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
new file mode 100644
index 0000000..a34b55c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class SetAudioVolumeLevelDiscoveryActionTest {
+    private HdmiControlService mHdmiControlServiceSpy;
+    private HdmiCecController mHdmiCecController;
+    private HdmiCecLocalDevicePlayback mPlaybackDevice;
+    private FakeNativeWrapper mNativeWrapper;
+    private FakePowerManagerWrapper mPowerManager;
+    private Looper mLooper;
+    private Context mContextSpy;
+    private TestLooper mTestLooper = new TestLooper();
+    private int mPhysicalAddress = 0x1100;
+    private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mPlaybackLogicalAddress;
+
+    private TestCallback mTestCallback;
+    private SetAudioVolumeLevelDiscoveryAction mAction;
+
+    /**
+     * Setup: Local Playback device attempts to determine whether a connected TV supports
+     * <Set Audio Volume Level>.
+     */
+    @Before
+    public void setUp() throws RemoteException {
+        mContextSpy = spy(new ContextWrapper(
+                InstrumentationRegistry.getInstrumentation().getTargetContext()));
+
+        mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
+        doNothing().when(mHdmiControlServiceSpy)
+                .writeStringSystemProperty(anyString(), anyString());
+
+        mLooper = mTestLooper.getLooper();
+        mHdmiControlServiceSpy.setIoLooper(mLooper);
+        mHdmiControlServiceSpy.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+
+        mNativeWrapper = new FakeNativeWrapper();
+        mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+
+        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+                mHdmiControlServiceSpy, mNativeWrapper, mHdmiControlServiceSpy.getAtomWriter());
+        mHdmiControlServiceSpy.setCecController(mHdmiCecController);
+        mHdmiControlServiceSpy.setHdmiMhlController(
+                HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
+        mHdmiControlServiceSpy.initService();
+        mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+        mHdmiControlServiceSpy.setPowerManager(mPowerManager);
+
+        mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy);
+        mPlaybackDevice.init();
+        mLocalDevices.add(mPlaybackDevice);
+
+        mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        mTestLooper.dispatchAll();
+
+        synchronized (mPlaybackDevice.mLock) {
+            mPlaybackLogicalAddress = mPlaybackDevice.getDeviceInfo().getLogicalAddress();
+        }
+
+        // Setup specific to these tests
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                Constants.ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV));
+        mTestLooper.dispatchAll();
+
+        mTestCallback = new TestCallback();
+        mAction = new SetAudioVolumeLevelDiscoveryAction(mPlaybackDevice,
+                Constants.ADDR_TV, mTestCallback);
+    }
+
+    @Test
+    public void sendsSetAudioVolumeLevel() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage setAudioVolumeLevel = SetAudioVolumeLevelMessage.build(
+                mPlaybackLogicalAddress, Constants.ADDR_TV,
+                Constants.AUDIO_VOLUME_STATUS_UNKNOWN);
+        assertThat(mNativeWrapper.getResultMessages()).contains(setAudioVolumeLevel);
+    }
+
+    @Test
+    public void noMatchingFeatureAbortReceived_actionSucceedsAndSetsFeatureSupported() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        // Wrong opcode
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                Constants.ADDR_TV,
+                mPlaybackLogicalAddress,
+                Constants.MESSAGE_GIVE_DECK_STATUS,
+                Constants.ABORT_UNRECOGNIZED_OPCODE));
+        // Wrong source
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                Constants.ADDR_AUDIO_SYSTEM,
+                mPlaybackLogicalAddress,
+                Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                Constants.ABORT_UNRECOGNIZED_OPCODE));
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORTED);
+        assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    @Test
+    public void matchingFeatureAbortReceived_actionSucceedsAndSetsFeatureNotSupported() {
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
+                Constants.ADDR_TV,
+                mPlaybackLogicalAddress,
+                Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                Constants.ABORT_UNRECOGNIZED_OPCODE));
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_NOT_SUPPORTED);
+        assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    @Test
+    public void messageFailedToSend_actionFailsAndDoesNotUpdateFeatureSupport() {
+        mNativeWrapper.setMessageSendResult(Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
+                SendMessageResult.FAIL);
+        mTestLooper.dispatchAll();
+
+        mPlaybackDevice.addAndStartAction(mAction);
+        mTestLooper.dispatchAll();
+
+        @DeviceFeatures.FeatureSupportStatus int avcSupport =
+                mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+                        .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+        assertThat(avcSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
+        assertThat(mTestCallback.getResult()).isEqualTo(
+                HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+    }
+
+    private static class TestCallback extends IHdmiControlCallback.Stub {
+        private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+        @Override
+        public void onComplete(int result) {
+            mCallbackResult.add(result);
+        }
+
+        private int getResult() {
+            assertThat(mCallbackResult.size()).isEqualTo(1);
+            return mCallbackResult.get(0);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelMessageTest.java
new file mode 100644
index 0000000..0201c68
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelMessageTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
+import static com.android.server.hdmi.HdmiUtils.buildMessage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class SetAudioVolumeLevelMessageTest {
+    @Test
+    public void build_maxVolume() {
+        HdmiCecMessage message = SetAudioVolumeLevelMessage.build(
+                Constants.ADDR_TV, Constants.ADDR_PLAYBACK_1, 100);
+        assertThat(message.getValidationResult()).isEqualTo(HdmiCecMessageValidator.OK);
+        assertThat(message).isEqualTo(buildMessage("04:73:64"));
+    }
+
+    @Test
+    public void build_noVolumeChange() {
+        HdmiCecMessage message = SetAudioVolumeLevelMessage.build(
+                Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM, 0x7F);
+        assertThat(message.getValidationResult()).isEqualTo(HdmiCecMessageValidator.OK);
+        assertThat(message).isEqualTo(buildMessage("05:73:7F"));
+    }
+
+    @Test
+    public void build_invalid() {
+        assertThat(SetAudioVolumeLevelMessage
+                .build(Constants.ADDR_UNREGISTERED, Constants.ADDR_AUDIO_SYSTEM, 50)
+                .getValidationResult())
+                .isEqualTo(ERROR_SOURCE);
+        assertThat(SetAudioVolumeLevelMessage
+                .build(Constants.ADDR_TV, Constants.ADDR_BROADCAST, 50)
+                .getValidationResult())
+                .isEqualTo(ERROR_DESTINATION);
+        assertThat(HdmiUtils.buildMessage("04:73")
+                .getValidationResult())
+                .isEqualTo(ERROR_PARAMETER_SHORT);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index b34b853..9d14341 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -100,7 +100,6 @@
                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
         mHdmiControlService.setCecController(hdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
-        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceTv);
         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
         hdmiPortInfos[0] =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index b40650e..095c69c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -64,7 +64,7 @@
 
     @Before
     public void SetUp() {
-        mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+        mDeviceInfoForTests = HdmiDeviceInfo.hardwarePort(1001, 1234);
 
         Context context = InstrumentationRegistry.getTargetContext();
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
new file mode 100644
index 0000000..51ddcef
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 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.locksettings;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.PasswordMetrics;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
+import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** atest FrameworksServicesTests:WeakEscrowTokenTests */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WeakEscrowTokenTests extends BaseLockSettingsServiceTests{
+
+    @Test
+    public void testWeakTokenActivatedImmediatelyIfNoUserPassword()
+            throws RemoteException {
+        mockAutoHardware();
+        final byte[] token = "some-high-entropy-secure-token".getBytes();
+        IWeakEscrowTokenActivatedListener mockListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockListener);
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
+        verify(mockListener).onWeakEscrowTokenActivated(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokenActivatedLaterWithUserPassword()
+            throws RemoteException {
+        mockAutoHardware();
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        IWeakEscrowTokenActivatedListener mockListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        LockscreenCredential password = newPassword("password");
+        mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
+
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockListener);
+        // Token not activated immediately since user password exists
+        assertFalse(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        // Activate token (password gets migrated to SP at the same time)
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+        // Verify token is activated and valid
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
+        verify(mockListener).onWeakEscrowTokenActivated(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokensRemovedIfCredentialChanged() throws Exception {
+        mockAutoHardware();
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        IWeakEscrowTokenRemovedListener mockRemoveListener = mockAliveRemoveListener();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        LockscreenCredential password = newPassword("password");
+        LockscreenCredential pattern = newPattern("123654");
+        mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
+        mService.registerWeakEscrowTokenRemovedListener(mockRemoveListener);
+
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockActivateListener);
+
+        // Activate token
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+
+        // Verify token removed
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mLocalService.setLockCredentialWithToken(
+                pattern, handle, token, PRIMARY_USER_ID));
+        assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+        verify(mockRemoveListener).onWeakEscrowTokenRemoved(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokenRemovedListenerRegistered() throws Exception {
+        mockAutoHardware();
+        IWeakEscrowTokenRemovedListener mockRemoveListener = mockAliveRemoveListener();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockActivateListener);
+
+        mService.registerWeakEscrowTokenRemovedListener(mockRemoveListener);
+        mService.removeWeakEscrowToken(handle, PRIMARY_USER_ID);
+
+        verify(mockRemoveListener).onWeakEscrowTokenRemoved(handle, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testWeakTokenRemovedListenerUnregistered() throws Exception {
+        mockAutoHardware();
+        IWeakEscrowTokenRemovedListener mockRemoveListener = mockAliveRemoveListener();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        byte[] token0 = "some-high-entropy-secure-token-0".getBytes();
+        byte[] token1 = "some-high-entropy-secure-token-1".getBytes();
+        long handle0 = mService.addWeakEscrowToken(token0, PRIMARY_USER_ID, mockActivateListener);
+        long handle1 = mService.addWeakEscrowToken(token1, PRIMARY_USER_ID, mockActivateListener);
+
+        mService.registerWeakEscrowTokenRemovedListener(mockRemoveListener);
+        mService.removeWeakEscrowToken(handle0, PRIMARY_USER_ID);
+        verify(mockRemoveListener).onWeakEscrowTokenRemoved(handle0, PRIMARY_USER_ID);
+
+        mService.unregisterWeakEscrowTokenRemovedListener(mockRemoveListener);
+        mService.removeWeakEscrowToken(handle1, PRIMARY_USER_ID);
+        verify(mockRemoveListener, never()).onWeakEscrowTokenRemoved(handle1, PRIMARY_USER_ID);
+    }
+
+    @Test
+    public void testUnlockUserWithToken_weakEscrowToken() throws Exception {
+        mockAutoHardware();
+        IWeakEscrowTokenActivatedListener mockActivateListener =
+                mock(IWeakEscrowTokenActivatedListener.Stub.class);
+        LockscreenCredential password = newPassword("password");
+        byte[] token = "some-high-entropy-secure-token".getBytes();
+        mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
+        // Disregard any reportPasswordChanged() invocations as part of credential setup.
+        flushHandlerTasks();
+        reset(mDevicePolicyManager);
+
+        long handle = mService.addWeakEscrowToken(token, PRIMARY_USER_ID, mockActivateListener);
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+        assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
+        assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
+
+        mService.onCleanupUser(PRIMARY_USER_ID);
+        assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
+
+        assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
+        assertEquals(PasswordMetrics.computeForCredential(password),
+                mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
+    }
+
+    private void mockAutoHardware() {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
+    }
+
+    private IWeakEscrowTokenRemovedListener mockAliveRemoveListener() {
+        IWeakEscrowTokenRemovedListener mockListener =
+                mock(IWeakEscrowTokenRemovedListener.Stub.class);
+        IBinder mockIBinder = mock(IBinder.class);
+        when(mockIBinder.isBinderAlive()).thenReturn(true);
+        when(mockListener.asBinder()).thenReturn(mockIBinder);
+        return mockListener;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index b811e28..9a6f61e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -49,12 +49,8 @@
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -75,6 +71,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
 import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
+import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -108,6 +105,8 @@
 import android.app.IUidObserver;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -125,8 +124,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
 import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.wifi.WifiInfo;
@@ -138,7 +135,6 @@
 import android.os.PowerSaveState;
 import android.os.RemoteException;
 import android.os.SimpleClock;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
@@ -263,12 +259,13 @@
     private @Mock CarrierConfigManager mCarrierConfigManager;
     private @Mock TelephonyManager mTelephonyManager;
     private @Mock UserManager mUserManager;
+    private @Mock NetworkStatsManager mStatsManager;
+    private TestDependencies mDeps;
 
     private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
 
     private ActivityManagerInternal mActivityManagerInternal;
-    private NetworkStatsManagerInternal mStatsService;
 
     private IUidObserver mUidObserver;
     private INetworkManagementEventObserver mNetworkObserver;
@@ -335,8 +332,47 @@
                 .setBatterySaverEnabled(false).build();
         final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
         when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
+    }
 
-        mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
+    private class TestDependencies extends NetworkPolicyManagerService.Dependencies {
+        private final SparseArray<NetworkStats.Bucket> mMockedStats = new SparseArray<>();
+
+        TestDependencies(Context context) {
+            super(context);
+        }
+
+        @Override
+        long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+            int total = 0;
+            for (int i = 0; i < mMockedStats.size(); i++) {
+                NetworkStats.Bucket bucket = mMockedStats.valueAt(i);
+                total += bucket.getRxBytes() + bucket.getTxBytes();
+            }
+            return total;
+        }
+
+        @Override
+        List<NetworkStats.Bucket> getNetworkUidBytes(NetworkTemplate template, long start,
+                long end) {
+            final List<NetworkStats.Bucket> ret = new ArrayList<>();
+            for (int i = 0; i < mMockedStats.size(); i++) {
+                ret.add(mMockedStats.valueAt(i));
+            }
+            return ret;
+        }
+
+        private void setMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+            final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
+            when(bucket.getUid()).thenReturn(uid);
+            when(bucket.getRxBytes()).thenReturn(rxBytes);
+            when(bucket.getTxBytes()).thenReturn(txBytes);
+            mMockedStats.set(uid, bucket);
+        }
+
+        private void increaseMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+            final NetworkStats.Bucket bucket = mMockedStats.get(uid);
+            setMockedTotalBytes(uid, bucket.getRxBytes() + rxBytes, bucket.getTxBytes() + txBytes);
+        }
     }
 
     @Before
@@ -376,6 +412,8 @@
                         return mConnManager;
                     case Context.USER_SERVICE:
                         return mUserManager;
+                    case Context.NETWORK_STATS_SERVICE:
+                        return mStatsManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -400,8 +438,9 @@
         }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
 
         mFutureIntent = newRestrictBackgroundChangedFuture();
+        mDeps = new TestDependencies(mServiceContext);
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
-                mNetworkManager, mIpm, mClock, mPolicyDir, true);
+                mNetworkManager, mIpm, mClock, mPolicyDir, true, mDeps);
         mService.bindConnectivityManager();
         mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
@@ -456,6 +495,9 @@
         verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
 
+        // Simulate NetworkStatsService broadcast stats updated to signal its readiness.
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_UPDATED));
+
         NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
         mDefaultWarningBytes = defaultPolicy.warningBytes;
         mDefaultLimitBytes = defaultPolicy.limitBytes;
@@ -479,7 +521,6 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.removeServiceForTest(AppStandbyInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
-        LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
     }
 
     @After
@@ -1108,10 +1149,7 @@
         when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
 
         // pretend that 512 bytes total have happened
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
-        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
-                .thenReturn(stats.getTotalBytes());
+        mDeps.setMockedTotalBytes(UID_A, 256L, 256L);
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
         setNetworkPolicies(new NetworkPolicy(
@@ -1124,26 +1162,6 @@
 
     @Test
     public void testNotificationWarningLimitSnooze() throws Exception {
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1161,9 +1179,7 @@
 
         // Normal usage means no notification
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1178,9 +1194,7 @@
 
         // Push over warning
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1196,9 +1210,7 @@
 
         // Push over warning, but with a config that isn't from an identified carrier
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1215,9 +1227,7 @@
 
         // Push over limit
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1810), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1248,26 +1258,6 @@
 
     @Test
     public void testNotificationRapid() throws Exception {
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1285,9 +1275,7 @@
 
         // Using 20% data in 20% time is normal
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1297,16 +1285,9 @@
         // Using 80% data in 20% time is alarming; but spread equally among
         // three UIDs means we get generic alert
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
-            stats.clear();
-            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_C, DataUnit.MEGABYTES.toBytes(480), 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1325,14 +1306,9 @@
         // Using 80% data in 20% time is alarming; but mostly done by one UID
         // means we get specific alert
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
-            stats.clear();
-            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(960), 0);
+            mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_C, 0, 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1362,13 +1338,10 @@
 
         // bring up wifi network with metered policy
         snapshots = List.of(buildWifi());
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
+        mDeps.setMockedTotalBytes(UID_A, 0L, 0L);
 
         {
             when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(
@@ -1647,18 +1620,6 @@
         final NetworkPolicyManagerInternal internal = LocalServices
                 .getService(NetworkPolicyManagerInternal.class);
 
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(invocation -> {
-                    final NetworkStatsHistory.Entry entry = history.getValues(
-                            invocation.getArgument(1), invocation.getArgument(2), null);
-                    return entry.rxBytes + entry.txBytes;
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1669,9 +1630,7 @@
         setCurrentTimeMillis(end);
 
         // Get some data usage in place
-        history.clear();
-        history.recordData(start, end,
-                new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+        mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
         // No data plan
         {
@@ -1786,22 +1745,11 @@
                 true);
     }
 
-    private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
-        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
-                rxBytes, 1, txBytes, 1, 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats.getTotalBytes());
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
-    }
-
     private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
-        npmi.onStatsProviderWarningOrLimitReached("TEST");
+        mService.onStatsProviderWarningOrLimitReached();
         // Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
         postMsgAndWaitForCompletion();
-        verify(mStatsService).forceUpdate();
+        verify(mStatsManager).forceUpdate();
         // Wait for processing of MSG_*_INTERFACE_QUOTAS.
         postMsgAndWaitForCompletion();
     }
@@ -1814,13 +1762,12 @@
     public void testStatsProviderWarningAndLimitReached() throws Exception {
         final int CYCLE_DAY = 15;
 
-        final NetworkStats stats = new NetworkStats(0L, 1);
-        increaseMockedTotalBytes(stats, 2999, 2000);
+        mDeps.setMockedTotalBytes(UID_A, 2999, 2000);
 
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
                 Long.MAX_VALUE);
 
         // Set warning to 7KB and limit to 10KB.
@@ -1830,32 +1777,32 @@
         postMsgAndWaitForCompletion();
 
         // Verifies that remaining quotas are set to providers.
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
+        reset(mStatsManager);
 
         // Increase the usage and simulates that limit reached fires earlier by provider,
         // but actually the quota is not yet reached. Verifies that the limit reached leads to
         // a force update and new quotas should be set.
-        increaseMockedTotalBytes(stats, 1000, 999);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000, 999);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+        reset(mStatsManager);
 
         // Increase the usage and simulate warning reached, the new warning should be unlimited
         // since service will disable warning quota to stop lower layer from keep triggering
         // warning reached event.
-        increaseMockedTotalBytes(stats, 1000L, 1000);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(
                 TEST_IFACE, Long.MAX_VALUE, 1002L);
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // Increase the usage that over the warning and limit, the new limit should set to 1 to
         // block the network traffic.
-        increaseMockedTotalBytes(stats, 1000L, 1000);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+        reset(mStatsManager);
     }
 
     private void enableRestrictedMode(boolean enable) throws Exception {
@@ -2145,7 +2092,7 @@
     }
 
     private void verifyAdvisePersistThreshold() throws Exception {
-        verify(mStatsService).advisePersistThreshold(anyLong());
+        verify(mStatsManager).advisePersistThreshold(anyLong());
     }
 
     private static class TestAbstractFuture<T> extends AbstractFuture<T> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index e4273dc..429445f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -315,8 +315,8 @@
         mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
                 asHandle(currentUser));
         try {
-            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
-                    /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+            assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                    /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
                     asHandle(currentUser));
@@ -335,8 +335,8 @@
                 asHandle(currentUser));
         try {
             synchronized (mUserRemoveLock) {
-                assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
-                        /* evenWhenDisallowed= */ true))
+                assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                        /* overrideDevicePolicy= */ true))
                                 .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
                 waitForUserRemovalLocked(user1.id);
             }
@@ -352,8 +352,8 @@
     @MediumTest
     @Test
     public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
-        assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM,
-                /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+        assertThat(mUserManager.removeUserWhenPossible(UserHandle.SYSTEM,
+                /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
 
         assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
     }
@@ -362,8 +362,8 @@
     @Test
     public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
         assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
-        assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE,
-                /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
+        assertThat(mUserManager.removeUserWhenPossible(UserHandle.of(Integer.MAX_VALUE),
+                /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_ERROR);
     }
 
     @MediumTest
@@ -374,8 +374,8 @@
         // Switch to the user just created.
         switchUser(user1.id, null, /* ignoreHandle= */ true);
 
-        assertThat(mUserManager.removeUserOrSetEphemeral(user1.id, /* evenWhenDisallowed= */ false))
-                .isEqualTo(UserManager.REMOVE_RESULT_DEFERRED);
+        assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                /* overrideDevicePolicy= */ false)).isEqualTo(UserManager.REMOVE_RESULT_DEFERRED);
 
         assertThat(hasUser(user1.id)).isTrue();
         assertThat(getUser(user1.id).isEphemeral()).isTrue();
@@ -395,8 +395,9 @@
     public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
         final UserInfo user1 = createUser("User 1", /* flags= */ 0);
         synchronized (mUserRemoveLock) {
-            assertThat(mUserManager.removeUserOrSetEphemeral(user1.id,
-                    /* evenWhenDisallowed= */ false)).isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
+            assertThat(mUserManager.removeUserWhenPossible(user1.getUserHandle(),
+                    /* overrideDevicePolicy= */ false))
+                            .isEqualTo(UserManager.REMOVE_RESULT_REMOVED);
             waitForUserRemovalLocked(user1.id);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 761cea7..90b19a4 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -160,12 +160,12 @@
     }
 
     @Test
-    public void create_stateWithCancelStickyRequestFlag() {
+    public void create_stateWithCancelOverrideRequestFlag() {
         String configString = "<device-state-config>\n"
                 + "    <device-state>\n"
                 + "        <identifier>1</identifier>\n"
                 + "        <flags>\n"
-                + "            <flag>FLAG_CANCEL_STICKY_REQUESTS</flag>\n"
+                + "            <flag>FLAG_CANCEL_OVERRIDE_REQUESTS</flag>\n"
                 + "        </flags>\n"
                 + "        <conditions/>\n"
                 + "    </device-state>\n"
@@ -183,7 +183,7 @@
 
         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         final DeviceState[] expectedStates = new DeviceState[]{
-                new DeviceState(1, "", DeviceState.FLAG_CANCEL_STICKY_REQUESTS),
+                new DeviceState(1, "", DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS),
                 new DeviceState(2, "", 0 /* flags */) };
         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
     }
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index e47a07c..c832a3e 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -73,7 +73,7 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -138,7 +138,6 @@
     private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
 
     private PowerManagerService mService;
-    private PowerSaveState mPowerSaveState;
     private DisplayPowerRequest mDisplayPowerRequest;
     private ContextWrapper mContextSpy;
     private BatteryReceiver mBatteryReceiver;
@@ -147,7 +146,7 @@
     private OffsettableClock mClock;
     private TestLooper mTestLooper;
 
-    private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
+    private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
         private final IntentFilter mFilter;
 
         IntentFilterMatcher(IntentFilter filter) {
@@ -173,13 +172,13 @@
         MockitoAnnotations.initMocks(this);
         FakeSettingsProvider.clearSettingsProvider();
 
-        mPowerSaveState = new PowerSaveState.Builder()
+        PowerSaveState powerSaveState = new PowerSaveState.Builder()
                 .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
                 .setBrightnessFactor(BRIGHTNESS_FACTOR)
                 .build();
         when(mBatterySaverPolicyMock.getBatterySaverPolicy(
                 eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
-                .thenReturn(mPowerSaveState);
+                .thenReturn(powerSaveState);
         when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
         when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
@@ -195,7 +194,7 @@
         addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
         addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
 
-        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
 
@@ -304,8 +303,8 @@
         LocalServices.addService(clazz, mock);
     }
 
-    private void startSystem() throws Exception {
-        mService.systemReady(null);
+    private void startSystem() {
+        mService.systemReady();
 
         // Grab the BatteryReceiver
         ArgumentCaptor<BatteryReceiver> batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class);
@@ -403,9 +402,9 @@
     }
 
     @Test
-    public void testGetDesiredScreenPolicy_WithVR() throws Exception {
+    public void testGetDesiredScreenPolicy_WithVR() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         // Brighten up the screen
         mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
                 null, null);
@@ -436,13 +435,13 @@
     }
 
     @Test
-    public void testWakefulnessAwake_InitialValue() throws Exception {
+    public void testWakefulnessAwake_InitialValue() {
         createService();
         assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
     }
 
     @Test
-    public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+    public void testWakefulnessSleep_NoDozeSleepFlag() {
         createService();
         // Start with AWAKE state
         startSystem();
@@ -455,7 +454,7 @@
     }
 
     @Test
-    public void testWakefulnessAwake_AcquireCausesWakeup() throws Exception {
+    public void testWakefulnessAwake_AcquireCausesWakeup() {
         createService();
         startSystem();
         forceSleep();
@@ -487,7 +486,7 @@
     }
 
     @Test
-    public void testWakefulnessAwake_IPowerManagerWakeUp() throws Exception {
+    public void testWakefulnessAwake_IPowerManagerWakeUp() {
         createService();
         startSystem();
         forceSleep();
@@ -501,9 +500,7 @@
      * or docked.
      */
     @Test
-    public void testWakefulnessAwake_ShouldWakeUpWhenPluggedIn() throws Exception {
-        boolean powerState;
-
+    public void testWakefulnessAwake_ShouldWakeUpWhenPluggedIn() {
         createService();
         startSystem();
         forceSleep();
@@ -579,7 +576,7 @@
     }
 
     @Test
-    public void testWakefulnessDoze_goToSleep() throws Exception {
+    public void testWakefulnessDoze_goToSleep() {
         createService();
         // Start with AWAKE state
         startSystem();
@@ -595,7 +592,7 @@
     public void testWasDeviceIdleFor_true() {
         int interval = 1000;
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mService.onUserActivity();
         advanceTime(interval + 1 /* just a little more */);
@@ -606,7 +603,7 @@
     public void testWasDeviceIdleFor_false() {
         int interval = 1000;
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
@@ -615,7 +612,7 @@
     @Test
     public void testForceSuspend_putsDeviceToSleep() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Verify that we start awake
@@ -636,7 +633,7 @@
     }
 
     @Test
-    public void testForceSuspend_pakeLocksDisabled() {
+    public void testForceSuspend_wakeLocksDisabled() {
         final String tag = "TestWakelockTag_098213";
         final int flags = PowerManager.PARTIAL_WAKE_LOCK;
         final String pkg = mContextSpy.getOpPackageName();
@@ -661,7 +658,7 @@
         //
         // TEST STARTS HERE
         //
-        mService.systemReady(null);
+        mService.systemReady();
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         // Verify that we start awake
@@ -686,7 +683,7 @@
     }
 
     @Test
-    public void testForceSuspend_forceSuspendFailurePropagated() throws Exception {
+    public void testForceSuspend_forceSuspendFailurePropagated() {
         createService();
         startSystem();
         when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false);
@@ -694,7 +691,7 @@
     }
 
     @Test
-    public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() throws Exception {
+    public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() {
         final String suspendBlockerName = "PowerManagerService.Display";
         final String tag = "acq_causes_wakeup";
         final String packageName = "pkg.name";
@@ -741,7 +738,7 @@
     }
 
     @Test
-    public void testSuspendBlockerHeldDuringBoot() throws Exception {
+    public void testSuspendBlockerHeldDuringBoot() {
         final String suspendBlockerName = "PowerManagerService.Booting";
 
         final boolean[] isAcquired = new boolean[1];
@@ -760,7 +757,7 @@
         createService();
         assertTrue(isAcquired[0]);
 
-        mService.systemReady(null);
+        mService.systemReady();
         assertTrue(isAcquired[0]);
 
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -768,7 +765,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception {
+    public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(120);
         setAttentiveTimeout(100);
@@ -788,7 +785,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_hideWarningIfInattentiveSleepIsDisabled() throws Exception {
+    public void testInattentiveSleep_hideWarningIfInattentiveSleepIsDisabled() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(120);
         setAttentiveTimeout(100);
@@ -807,7 +804,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_userActivityDismissesWarning() throws Exception {
+    public void testInattentiveSleep_userActivityDismissesWarning() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -833,7 +830,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_warningHiddenAfterWakingUp() throws Exception {
+    public void testInattentiveSleep_warningHiddenAfterWakingUp() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveWarningDuration(70);
         setAttentiveTimeout(100);
@@ -851,7 +848,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_noWarningShownIfInattentiveSleepDisabled() throws Exception {
+    public void testInattentiveSleep_noWarningShownIfInattentiveSleepDisabled() {
         setAttentiveTimeout(-1);
         createService();
         startSystem();
@@ -859,7 +856,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_goesToSleepAfterTimeout() throws Exception {
+    public void testInattentiveSleep_goesToSleepAfterTimeout() {
         setMinimumScreenOffTimeoutConfig(5);
         setAttentiveTimeout(5);
         createService();
@@ -871,7 +868,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception {
+    public void testInattentiveSleep_goesToSleepWithWakeLock() {
         final String pkg = mContextSpy.getOpPackageName();
         final Binder token = new Binder();
         final String tag = "testInattentiveSleep_goesToSleepWithWakeLock";
@@ -893,8 +890,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_wakeLockOnAfterRelease_inattentiveSleepTimeoutNotAffected()
-            throws Exception {
+    public void testInattentiveSleep_wakeLockOnAfterRelease_inattentiveSleepTimeoutNotAffected() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -922,8 +918,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_userActivityNoChangeLights_inattentiveSleepTimeoutNotAffected()
-            throws Exception {
+    public void testInattentiveSleep_userActivityNoChangeLights_inattentiveSleepTimeoutNotAffected() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -945,8 +940,7 @@
     }
 
     @Test
-    public void testInattentiveSleep_userActivity_inattentiveSleepTimeoutExtended()
-            throws Exception {
+    public void testInattentiveSleep_userActivity_inattentiveSleepTimeoutExtended() {
         final DisplayInfo info = new DisplayInfo();
         info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
         when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
@@ -965,7 +959,7 @@
     }
 
     @Test
-    public void testWakeLock_affectsProperDisplayGroup() throws Exception {
+    public void testWakeLock_affectsProperDisplayGroup() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1005,7 +999,7 @@
     }
 
     @Test
-    public void testInvalidDisplayGroupWakeLock_affectsAllDisplayGroups() throws Exception {
+    public void testInvalidDisplayGroupWakeLock_affectsAllDisplayGroups() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1045,7 +1039,7 @@
     }
 
     @Test
-    public void testRemovedDisplayGroupWakeLock_affectsNoDisplayGroups() throws Exception {
+    public void testRemovedDisplayGroupWakeLock_affectsNoDisplayGroups() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
@@ -1086,7 +1080,7 @@
     }
 
     @Test
-    public void testBoot_ShouldBeAwake() throws Exception {
+    public void testBoot_ShouldBeAwake() {
         createService();
         startSystem();
 
@@ -1095,7 +1089,7 @@
     }
 
     @Test
-    public void testBoot_DesiredScreenPolicyShouldBeBright() throws Exception {
+    public void testBoot_DesiredScreenPolicyShouldBeBright() {
         createService();
         startSystem();
 
@@ -1104,7 +1098,7 @@
     }
 
     @Test
-    public void testQuiescentBoot_ShouldBeAsleep() throws Exception {
+    public void testQuiescentBoot_ShouldBeAsleep() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
         startSystem();
@@ -1115,7 +1109,7 @@
     }
 
     @Test
-    public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception {
+    public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
         startSystem();
@@ -1124,7 +1118,7 @@
     }
 
     @Test
-    public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() throws Exception {
+    public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
         startSystem();
@@ -1134,11 +1128,10 @@
     }
 
     @Test
-    public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted()
-            throws Exception {
+    public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
@@ -1150,7 +1143,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplayAvailable_available() throws Exception {
+    public void testIsAmbientDisplayAvailable_available() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
 
@@ -1158,7 +1151,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplayAvailable_unavailable() throws Exception {
+    public void testIsAmbientDisplayAvailable_unavailable() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
 
@@ -1166,14 +1159,14 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_default_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_default_notSuppressed() {
         createService();
 
         assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse();
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_suppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
 
@@ -1181,7 +1174,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
 
@@ -1189,7 +1182,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_multipleTokens_suppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_multipleTokens_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true);
@@ -1198,7 +1191,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressed_multipleTokens_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressed_multipleTokens_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
@@ -1207,7 +1200,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_default_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_default_notSuppressed() {
         createService();
 
         assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test"))
@@ -1215,7 +1208,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_suppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
 
@@ -1224,7 +1217,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_notSuppressed() throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
 
@@ -1233,8 +1226,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_suppressed()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_suppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true);
@@ -1246,8 +1238,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_notSuppressed()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForToken_multipleTokens_notSuppressed() {
         createService();
         mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
         mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
@@ -1259,8 +1250,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_ambientDisplayUnavailable()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_ambientDisplayUnavailable() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
 
@@ -1270,8 +1260,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_default()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_default() {
         createService();
 
         BinderService service = mService.getBinderServiceInstance();
@@ -1280,8 +1269,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_suppressedByCallingApp()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_suppressedByCallingApp() {
         createService();
         BinderService service = mService.getBinderServiceInstance();
         service.suppressAmbientDisplay("test", true);
@@ -1294,8 +1282,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_notSuppressedByCallingApp()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_notSuppressedByCallingApp() {
         createService();
         BinderService service = mService.getBinderServiceInstance();
         service.suppressAmbientDisplay("test", false);
@@ -1308,8 +1295,7 @@
     }
 
     @Test
-    public void testIsAmbientDisplaySuppressedForTokenByApp_multipleTokensSuppressedByCallingApp()
-            throws Exception {
+    public void testIsAmbientDisplaySuppressedForTokenByApp_multipleTokensSuppressedByCallingApp() {
         createService();
         BinderService service = mService.getBinderServiceInstance();
         service.suppressAmbientDisplay("test1", true);
@@ -1358,7 +1344,7 @@
     @Test
     public void testSetPowerBoost_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         mService.getBinderServiceInstance().setPowerBoost(Boost.INTERACTION, 1234);
 
@@ -1368,7 +1354,7 @@
     @Test
     public void testSetPowerMode_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         // Enabled launch boost in BatterySaverController to allow setting launch mode.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(false);
@@ -1384,7 +1370,7 @@
     @Test
     public void testSetPowerMode_withLaunchBoostDisabledAndModeLaunch_ignoresCallToEnable() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1400,7 +1386,7 @@
     @Test
     public void testSetPowerModeChecked_returnsNativeCallResult() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1419,7 +1405,7 @@
     }
 
     @Test
-    public void testMultiDisplay_wakefulnessUpdates() throws Exception {
+    public void testMultiDisplay_wakefulnessUpdates() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1448,7 +1434,7 @@
     }
 
     @Test
-    public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception {
+    public void testMultiDisplay_addDisplayGroup_preservesWakefulness() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1472,7 +1458,7 @@
     }
 
     @Test
-    public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception {
+    public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() {
         final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
         final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
                 new AtomicReference<>();
@@ -1502,7 +1488,7 @@
     @Test
     public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
                 .thenReturn(mockReturnConfig);
@@ -1517,7 +1503,7 @@
     @Test
     public void testSetFullPowerSavePolicy_callsStateMachine() {
         createService();
-        mService.systemReady(null);
+        mService.systemReady();
         BatterySaverPolicyConfig mockSetPolicyConfig =
                 new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/wm/OWNERS b/services/tests/servicestests/src/com/android/server/wm/OWNERS
new file mode 100644
index 0000000..361760d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
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 d831903..a192bf8 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2701,6 +2701,8 @@
 
         // should trigger a broadcast
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+        Thread.sleep(500);
+        waitForIdle();
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
 
@@ -2728,6 +2730,8 @@
 
         // should trigger a broadcast
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+        Thread.sleep(500);
+        waitForIdle();
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 1362628..f6400b6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -601,6 +601,8 @@
         when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);
 
         mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+        Thread.sleep(500);
+        waitForIdle();
 
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -616,6 +618,8 @@
         when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
 
         mService.mAppOpsCallback.opChanged(0, mUid, PKG);
+        Thread.sleep(500);
+        waitForIdle();
 
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d49cf67..b48c9c3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -542,6 +542,7 @@
         mHelper.setInvalidMessageSent(PKG_P, UID_P);
         mHelper.setValidMessageSent(PKG_P, UID_P);
         mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
+        mHelper.setValidBubbleSent(PKG_P, UID_P);
 
         mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
 
@@ -561,6 +562,7 @@
         assertFalse(mHelper.hasSentInvalidMsg(PKG_N_MR1, UID_N_MR1));
         assertTrue(mHelper.hasSentValidMsg(PKG_P, UID_P));
         assertTrue(mHelper.didUserEverDemoteInvalidMsgApp(PKG_P, UID_P));
+        assertTrue(mHelper.hasSentValidBubble(PKG_P, UID_P));
         assertEquals(channel1,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         compareChannels(channel2,
@@ -3174,6 +3176,19 @@
     }
 
     @Test
+    public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception {
+        NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+        group.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+
+        NotificationChannelGroup group3 = group.clone();
+        group3.setBlocked(false);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true);
+        assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
+    }
+
+    @Test
     public void testIsGroup_appCannotResetBlock() throws Exception {
         NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
@@ -4706,7 +4721,7 @@
     public void testGetConversations_noDisabledGroups() {
         NotificationChannelGroup group = new NotificationChannelGroup("a", "a");
         group.setBlocked(true);
-        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false);
         NotificationChannel parent = new NotificationChannel("parent", "p", 1);
         mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
 
@@ -4927,6 +4942,18 @@
     }
 
     @Test
+    public void testValidBubbleSent() {
+        // create package preferences
+        mHelper.canShowBadge(PKG_P, UID_P);
+        // false by default
+        assertFalse(mHelper.hasSentValidBubble(PKG_P, UID_P));
+
+        // set something valid was sent
+        mHelper.setValidBubbleSent(PKG_P, UID_P);
+        assertTrue(mHelper.hasSentValidBubble(PKG_P, UID_P));
+    }
+
+    @Test
     public void testPullPackageChannelPreferencesStats() {
         String channelId = "parent";
         String name = "messages";
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 6970005..2b131e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -322,4 +322,26 @@
         assertFalse(navBarSource.getFrame().isEmpty());
         assertTrue(imeSource.getFrame().contains(navBarSource.getFrame()));
     }
+
+    @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
+    @Test
+    public void testInsetsGivenContentFrame() {
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 1000;
+        displayInfo.logicalHeight = 2000;
+        displayInfo.rotation = ROTATION_0;
+
+        WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
+        displayPolicy.addWindowLw(mNavBarWindow, attrs);
+        mNavBarWindow.setRequestedSize(attrs.width, attrs.height);
+        mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
+
+        mNavBarWindow.mGivenContentInsets.set(0, 10, 0, 0);
+
+        displayPolicy.layoutWindowLw(mNavBarWindow, null, mDisplayContent.mDisplayFrames);
+        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        final InsetsSource navBarSource = state.peekSource(ITYPE_NAVIGATION_BAR);
+        assertEquals(attrs.height - 10, navBarSource.getFrame().height());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
new file mode 100644
index 0000000..6e11d8c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.view.Display;
+import android.window.DisplayWindowPolicyController;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Tests for the {@link DisplayWindowPolicyControllerHelper} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:DisplayWindowPolicyControllerHelperTests
+ */
+@RunWith(WindowTestRunner.class)
+public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase {
+    private static final int TEST_USER_0_ID = 0;
+    private static final int TEST_USER_1_ID = 10;
+
+    private TestDisplayWindowPolicyController mDwpc = new TestDisplayWindowPolicyController();
+    private DisplayContent mSecondaryDisplay;
+
+    @Before
+    public void setUp() {
+        doReturn(mDwpc).when(mWm.mDisplayManagerInternal)
+                .getDisplayWindowPolicyController(anyInt());
+        mSecondaryDisplay = createNewDisplay();
+        assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
+        assertTrue(mSecondaryDisplay.mDwpcHelper.hasController());
+    }
+
+    @Test
+    public void testOnRunningActivityChanged() {
+        final ActivityRecord activity1 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_0_ID);
+        verifyTopActivityAndRunningUid(activity1,
+                true /* expectedUid0 */, false /* expectedUid1 */);
+        final ActivityRecord activity2 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_1_ID);
+        verifyTopActivityAndRunningUid(activity2,
+                true /* expectedUid0 */, true /* expectedUid1 */);
+        final ActivityRecord activity3 = launchActivityOnDisplay(mSecondaryDisplay, TEST_USER_0_ID);
+        verifyTopActivityAndRunningUid(activity3,
+                true /* expectedUid0 */, true /* expectedUid1 */);
+
+        activity3.finishing = true;
+        verifyTopActivityAndRunningUid(activity2,
+                true /* expectedUid0 */, true /* expectedUid1 */);
+
+        activity2.finishing = true;
+        verifyTopActivityAndRunningUid(activity1,
+                true /* expectedUid0 */, false /* expectedUid1 */);
+
+        activity1.finishing = true;
+        verifyTopActivityAndRunningUid(null /* expectedTopActivity */,
+                false /* expectedUid0 */, false /* expectedUid1 */);
+    }
+
+    private void verifyTopActivityAndRunningUid(ActivityRecord expectedTopActivity,
+            boolean expectedUid0, boolean expectedUid1) {
+        mSecondaryDisplay.onRunningActivityChanged();
+        int uidAmount = (expectedUid0 && expectedUid1) ? 2 : (expectedUid0 || expectedUid1) ? 1 : 0;
+        assertEquals(expectedTopActivity == null ? null :
+                expectedTopActivity.info.getComponentName(), mDwpc.mTopActivity);
+        assertEquals(expectedTopActivity == null ? UserHandle.USER_NULL :
+                expectedTopActivity.info.applicationInfo.uid, mDwpc.mTopActivityUid);
+        assertEquals(uidAmount, mDwpc.mRunningUids.size());
+        assertTrue(mDwpc.mRunningUids.contains(TEST_USER_0_ID) == expectedUid0);
+        assertTrue(mDwpc.mRunningUids.contains(TEST_USER_1_ID) == expectedUid1);
+
+    }
+
+    private ActivityRecord launchActivityOnDisplay(DisplayContent display, int uid) {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setDisplay(display)
+                .setUserId(uid)
+                .build();
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(task)
+                .setUid(uid)
+                .setOnTop(true)
+                .build();
+        return activity;
+    }
+
+    private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController {
+
+        ComponentName mTopActivity = null;
+        int mTopActivityUid = UserHandle.USER_NULL;
+        ArraySet<Integer> mRunningUids = new ArraySet<>();
+
+        @Override
+        public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+            return false;
+        }
+
+        @Override
+        public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
+                int systemWindowFlags) {
+            return false;
+        }
+
+        @Override
+        public void onTopActivityChanged(ComponentName topActivity, int uid) {
+            super.onTopActivityChanged(topActivity, uid);
+            mTopActivity = topActivity;
+            mTopActivityUid = uid;
+        }
+
+        @Override
+        public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
+            super.onRunningAppsChanged(runningUids);
+            mRunningUids.clear();
+            mRunningUids.addAll(runningUids);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 9e4cd16..365e749 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -51,6 +51,7 @@
 
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -447,6 +448,21 @@
         assertEquals(456, config.densityDpi);
     }
 
+    @Test
+    public void testDisplayRotationSettingsAppliedOnCreation() {
+        // Create new displays with different rotation settings
+        final SettingsEntry settingsEntry1 = new SettingsEntry();
+        settingsEntry1.mIgnoreOrientationRequest = false;
+        final DisplayContent dcDontIgnoreOrientation = createMockSimulatedDisplay(settingsEntry1);
+        final SettingsEntry settingsEntry2 = new SettingsEntry();
+        settingsEntry2.mIgnoreOrientationRequest = true;
+        final DisplayContent dcIgnoreOrientation = createMockSimulatedDisplay(settingsEntry2);
+
+        // Verify that newly created displays are created with correct rotation settings
+        assertFalse(dcDontIgnoreOrientation.getIgnoreOrientationRequest());
+        assertTrue(dcIgnoreOrientation.getIgnoreOrientationRequest());
+    }
+
     public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider {
         Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index ea5bf52..d3282b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -82,19 +81,6 @@
     }
 
     @Test
-    public void testControlsForDispatch_dockedTaskVisible() {
-        addWindow(TYPE_STATUS_BAR, "statusBar");
-        addWindow(TYPE_NAVIGATION_BAR, "navBar");
-
-        final WindowState win = createWindow(null, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
-        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
-
-        // The app must not control any system bars.
-        assertNull(controls);
-    }
-
-    @Test
     public void testControlsForDispatch_multiWindowTaskVisible() {
         addWindow(TYPE_STATUS_BAR, "statusBar");
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index b4c449a..632a59d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -117,8 +117,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
                 mFinishedCallback);
 
-        // Remove the app window so that the animation target can not be created
-        activity.removeImmediately();
+        // The activity doesn't contain window so the animation target cannot be created.
         mController.startAnimation();
 
         // Verify that the finish callback to reparent the leash is called
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 9ad8c5b..0debdfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,14 +21,17 @@
 import static com.android.server.wm.testing.Assert.assertThrows;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
@@ -471,6 +474,7 @@
         final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
                 .setOrganizer(mOrganizer)
+                .setFragmentToken(mFragmentToken)
                 .build();
 
         // Mock the task to invisible
@@ -485,4 +489,38 @@
         // Verifies that event was not sent
         verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
     }
+
+    /**
+     * Tests that a task fragment info changed event is still sent if the task is invisible only
+     * when the info changed event is because of the last activity in a task finishing.
+     */
+    @Test
+    public void testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent() {
+        // Create a TaskFragment with an activity, all within a parent task
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(mFragmentToken)
+                .setCreateParentTask()
+                .createActivityCount(1)
+                .build();
+        final Task parentTask = taskFragment.getTask();
+        final ActivityRecord activity = taskFragment.getTopNonFinishingActivity();
+        assertTrue(parentTask.shouldBeVisible(null));
+
+        // Dispatch pending info changed event from creating the activity
+        mController.registerOrganizer(mIOrganizer);
+        taskFragment.mTaskFragmentAppearedSent = true;
+        mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+        mController.dispatchPendingEvents();
+
+        // Finish the activity and verify that the task is invisible
+        activity.finishing = true;
+        assertFalse(parentTask.shouldBeVisible(null));
+
+        // Verify the info changed callback still occurred despite the task being invisible
+        reset(mOrganizer);
+        mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+        mController.dispatchPendingEvents();
+        verify(mOrganizer).onTaskFragmentInfoChanged(any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 3065e7d..8b0716c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -30,6 +30,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 
+import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -38,6 +39,8 @@
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
+import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
+
 class TestDisplayContent extends DisplayContent {
 
     public static final int DEFAULT_LOGICAL_DISPLAY_DENSITY = 300;
@@ -81,6 +84,7 @@
         protected final ActivityTaskManagerService mService;
         private boolean mSystemDecorations = false;
         private int mStatusBarHeight = 0;
+        private SettingsEntry mOverrideSettings;
 
         Builder(ActivityTaskManagerService service, int width, int height) {
             mService = service;
@@ -104,6 +108,10 @@
         private String generateUniqueId() {
             return "TEST_DISPLAY_CONTENT_" + System.currentTimeMillis();
         }
+        Builder setOverrideSettings(@Nullable SettingsEntry overrideSettings) {
+            mOverrideSettings = overrideSettings;
+            return this;
+        }
         Builder setSystemDecorations(boolean yes) {
             mSystemDecorations = yes;
             return this;
@@ -151,6 +159,11 @@
         TestDisplayContent build() {
             SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
 
+            if (mOverrideSettings != null) {
+                mService.mWindowManager.mDisplayWindowSettingsProvider
+                        .updateOverrideSettings(mInfo, mOverrideSettings);
+            }
+
             final int displayId = SystemServicesTestRule.sNextDisplayId++;
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index bec53d7..8b14e98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
@@ -77,6 +78,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
@@ -271,6 +273,22 @@
     }
 
     @Test
+    public void testRemoveImmediatelyClearsLeash() {
+        final AnimationAdapter animAdapter = mock(AnimationAdapter.class);
+        final WindowToken token = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+        final SurfaceControl.Transaction t = token.getPendingTransaction();
+        token.startAnimation(t, animAdapter, false /* hidden */,
+                SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
+        final ArgumentCaptor<SurfaceControl> leashCaptor =
+                ArgumentCaptor.forClass(SurfaceControl.class);
+        verify(animAdapter).startAnimation(leashCaptor.capture(), eq(t), anyInt(), any());
+        assertTrue(token.mSurfaceAnimator.hasLeash());
+        token.removeImmediately();
+        assertFalse(token.mSurfaceAnimator.hasLeash());
+        verify(t).remove(eq(leashCaptor.getValue()));
+    }
+
+    @Test
     public void testAddChildByIndex() {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 34038c5..59a2068 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -106,6 +106,7 @@
 
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
 import org.junit.After;
 import org.junit.Before;
@@ -720,18 +721,21 @@
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
     private DisplayContent createNewDisplayWithImeSupport(@DisplayImePolicy int imePolicy) {
-        return createNewDisplay(mDisplayInfo, imePolicy);
+        return createNewDisplay(mDisplayInfo, imePolicy, /* overrideSettings */ null);
     }
 
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
     DisplayContent createNewDisplay(DisplayInfo info) {
-        return createNewDisplay(info, DISPLAY_IME_POLICY_LOCAL);
+        return createNewDisplay(info, DISPLAY_IME_POLICY_LOCAL, /* overrideSettings */ null);
     }
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
-    private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy) {
+    private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy,
+            @Nullable SettingsEntry overrideSettings) {
         final DisplayContent display =
-                new TestDisplayContent.Builder(mAtm, info).build();
+                new TestDisplayContent.Builder(mAtm, info)
+                        .setOverrideSettings(overrideSettings)
+                        .build();
         final DisplayContent dc = display.mDisplayContent;
         // this display can show IME.
         dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy);
@@ -749,7 +753,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
         displayInfo.state = displayState;
-        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_LOCAL);
+        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_LOCAL, /* overrideSettings */ null);
     }
 
     /** Creates a {@link TestWindowState} */
@@ -761,11 +765,15 @@
 
     /** Creates a {@link DisplayContent} as parts of simulate display info for test. */
     DisplayContent createMockSimulatedDisplay() {
+        return createMockSimulatedDisplay(/* overrideSettings */ null);
+    }
+
+    DisplayContent createMockSimulatedDisplay(@Nullable SettingsEntry overrideSettings) {
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
         displayInfo.type = Display.TYPE_VIRTUAL;
         displayInfo.ownerUid = SYSTEM_UID;
-        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_FALLBACK_DISPLAY);
+        return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_FALLBACK_DISPLAY, overrideSettings);
     }
 
     IDisplayWindowInsetsController createDisplayWindowInsetsController() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6d8edc5..2036e64 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8646,10 +8646,10 @@
         /* Default value is 2 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
         sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
-        sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
-        sDefaults.putInt(
+        sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L);
+        sDefaults.putLong(
                 KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
-                120000);
+                120000L);
         sDefaults.putAll(ImsServiceEntitlement.getDefaults());
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5ad95b8..0c56de8 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12615,12 +12615,15 @@
         if (carriers == null || !SubscriptionManager.isValidPhoneId(slotIndex)) {
             return -1;
         }
-        // Execute the method setCarrierRestrictionRules with an empty excluded list and
-        // indicating priority for the allowed list.
+        // Execute the method setCarrierRestrictionRules with an empty excluded list.
+        // If the allowed list is empty, it means that all carriers are allowed (default allowed),
+        // otherwise it means that only specified carriers are allowed (default not allowed).
         CarrierRestrictionRules carrierRestrictionRules = CarrierRestrictionRules.newBuilder()
                 .setAllowedCarriers(carriers)
                 .setDefaultCarrierRestriction(
-                    CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)
+                    carriers.isEmpty()
+                        ? CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED
+                        : CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)
                 .build();
 
         int result = setCarrierRestrictionRules(carrierRestrictionRules);
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 464389b..30ca162 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -257,8 +257,6 @@
                 + mCardId
                 + ", mEid="
                 + mEid
-                + ", mIccId="
-                + SubscriptionInfo.givePrintableIccid(getIccId())
                 + ", mPhysicalSlotIndex="
                 + mPhysicalSlotIndex
                 + ", mIsRemovable="
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 2b1c8c8..17f34db 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -271,16 +271,13 @@
     @NonNull
     @Override
     public String toString() {
-        return "UiccSlotInfo (mIsActive="
-                + mIsActive
+        return "UiccSlotInfo ("
                 + ", mIsEuicc="
                 + mIsEuicc
                 + ", mCardId="
                 + mCardId
                 + ", cardState="
                 + mCardStateInfo
-                + ", phoneId="
-                + mLogicalSlotIdx
                 + ", mIsExtendedApduSupported="
                 + mIsExtendedApduSupported
                 + ", mIsRemovable="
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 53dff54..be233b8 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -42,6 +42,7 @@
 import android.telephony.ims.stub.SipTransportImplBase;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.internal.annotations.VisibleForTesting;
@@ -180,6 +181,12 @@
     // call ImsFeature#onFeatureRemoved.
     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
 
+    // A map of slot id -> boolean array, where each entry in the boolean array corresponds to an
+    // ImsFeature that was created for a slot id and not a sub id for backwards compatibility
+    // purposes.
+    private final SparseArray<SparseBooleanArray> mCreateImsFeatureWithSlotIdFlagMap =
+            new SparseArray<>();
+
     private IImsServiceControllerListener mListener;
     private Executor mExecutor;
 
@@ -222,15 +229,36 @@
         }
 
         @Override
-        public IImsMmTelFeature createMmTelFeature(int slotId) {
-            return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId),
-                "createMmTelFeature");
+        public IImsMmTelFeature createMmTelFeature(int slotId, int subId) {
+            MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
+            if (f == null) {
+                return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId, subId),
+                        "createMmTelFeature");
+            } else {
+                return f.getBinder();
+            }
         }
 
         @Override
-        public IImsRcsFeature createRcsFeature(int slotId) {
-            return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId),
-                "createRcsFeature");
+        public IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
+            MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
+            if (f == null) {
+                return executeMethodAsyncForResult(() -> createEmergencyOnlyMmTelFeatureInternal(
+                        slotId), "createEmergencyOnlyMmTelFeature");
+            } else {
+                return f.getBinder();
+            }
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, int subId) {
+            RcsFeature f  = (RcsFeature) getImsFeature(slotId, ImsFeature.FEATURE_RCS);
+            if (f == null) {
+                return executeMethodAsyncForResult(() ->
+                        createRcsFeatureInternal(slotId, subId), "createRcsFeature");
+            } else {
+                return f.getBinder();
+            }
         }
 
         @Override
@@ -248,9 +276,14 @@
         }
 
         @Override
-        public void removeImsFeature(int slotId, int featureType) {
+        public void removeImsFeature(int slotId, int featureType, boolean changeSubId) {
+            if (changeSubId && isImsFeatureCreatedForSlot(slotId, featureType)) {
+                Log.w(LOG_TAG, "Do not remove Ims feature for compatibility");
+                return;
+            }
             executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
                     "removeImsFeature");
+            setImsFeatureCreatedForSlot(slotId, featureType, false);
         }
 
         @Override
@@ -279,9 +312,10 @@
         }
 
         @Override
-        public IImsConfig getConfig(int slotId) {
+        public IImsConfig getConfig(int slotId, int subId) {
             return executeMethodAsyncForResult(() -> {
-                ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+                ImsConfigImplBase c =
+                        ImsService.this.getConfigForSubscription(slotId, subId);
                 if (c != null) {
                     c.setDefaultExecutor(mExecutor);
                     return c.getIImsConfig();
@@ -292,9 +326,10 @@
         }
 
         @Override
-        public IImsRegistration getRegistration(int slotId) {
+        public IImsRegistration getRegistration(int slotId, int subId) {
             return executeMethodAsyncForResult(() -> {
-                ImsRegistrationImplBase r =  ImsService.this.getRegistration(slotId);
+                ImsRegistrationImplBase r =
+                        ImsService.this.getRegistrationForSubscription(slotId, subId);
                 if (r != null) {
                     r.setDefaultExecutor(mExecutor);
                     return r.getBinder();
@@ -318,13 +353,15 @@
         }
 
         @Override
-        public void enableIms(int slotId) {
-            executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms");
+        public void enableIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.enableImsForSubscription(slotId, subId), "enableIms");
         }
 
         @Override
-        public void disableIms(int slotId) {
-            executeMethodAsync(() -> ImsService.this.disableIms(slotId), "disableIms");
+        public void disableIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.disableImsForSubscription(slotId, subId), "disableIms");
         }
 
         // Call the methods with a clean calling identity on the executor and wait indefinitely for
@@ -364,16 +401,8 @@
         return null;
     }
 
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    public SparseArray<ImsFeature> getFeatures(int slotId) {
-        return mFeaturesBySlot.get(slotId);
-    }
-
-    private IImsMmTelFeature createMmTelFeatureInternal(int slotId) {
-        MmTelFeature f = createMmTelFeature(slotId);
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) {
+        MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId);
         if (f != null) {
             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
             f.setDefaultExecutor(mExecutor);
@@ -384,8 +413,20 @@
         }
     }
 
-    private IImsRcsFeature createRcsFeatureInternal(int slotId) {
-        RcsFeature f = createRcsFeature(slotId);
+    private IImsMmTelFeature createEmergencyOnlyMmTelFeatureInternal(int slotId) {
+        MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+            f.setDefaultExecutor(mExecutor);
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId, int subI) {
+        RcsFeature f = createRcsFeatureForSubscription(slotId, subI);
         if (f != null) {
             f.setDefaultExecutor(mExecutor);
             setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
@@ -466,6 +507,49 @@
             f.onFeatureRemoved();
             features.remove(featureType);
         }
+
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsFeature getImsFeature(int slotId, int featureType) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                return null;
+            }
+            return features.get(featureType);
+        }
+    }
+
+    private void setImsFeatureCreatedForSlot(int slotId,
+            @ImsFeature.FeatureType int featureType, boolean createdForSlot) {
+        synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
+            getImsFeatureCreatedForSlot(slotId).put(featureType, createdForSlot);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean isImsFeatureCreatedForSlot(int slotId,
+            @ImsFeature.FeatureType int featureType) {
+        synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
+            return getImsFeatureCreatedForSlot(slotId).get(featureType);
+        }
+    }
+
+    private SparseBooleanArray getImsFeatureCreatedForSlot(int slotId) {
+        SparseBooleanArray createFlag = mCreateImsFeatureWithSlotIdFlagMap.get(slotId);
+        if (createFlag == null) {
+            createFlag = new SparseBooleanArray();
+            mCreateImsFeatureWithSlotIdFlagMap.put(slotId, createFlag);
+        }
+        return createFlag;
     }
 
     /**
@@ -524,27 +608,95 @@
     }
 
     /**
+     * The framework has enabled IMS for the subscription specified, the ImsService should register
+     * for IMS and perform all appropriate initialization to bring up all ImsFeatures.
+     *
+     * @param slotId The slot ID that IMS will be enabled for.
+     * @param subscriptionId The subscription ID that IMS will be enabled for.
+     */
+    public void enableImsForSubscription(int slotId, int subscriptionId) {
+        enableIms(slotId);
+    }
+
+    /**
+     * The framework has disabled IMS for the subscription specified. The ImsService must deregister
+     * for IMS and set capability status to false for all ImsFeatures.
+     * @param slotId The slot ID that IMS will be disabled for.
+     * @param subscriptionId The subscription ID that IMS will be disabled for.
+     */
+    public void disableImsForSubscription(int slotId, int subscriptionId) {
+        disableIms(slotId);
+    }
+
+    /**
      * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
      * and perform all appropriate initialization to bring up all ImsFeatures.
+     * @deprecated Use {@link #enableImsForSubscription} instead.
      */
+    @Deprecated
     public void enableIms(int slotId) {
     }
 
     /**
      * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
      * and set capability status to false for all ImsFeatures.
+     * @deprecated Use {@link #disableImsForSubscription} instead.
      */
+    @Deprecated
     public void disableIms(int slotId) {
     }
 
     /**
      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified subscription.
+     *
+     * @param subscriptionId The subscription ID that the MMTEL Feature is being created for.
+     * @return The newly created {@link MmTelFeature} associated with the subscription or null if
+     * the feature is not supported.
+     */
+    public @Nullable MmTelFeature createMmTelFeatureForSubscription(int slotId,
+            int subscriptionId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
+        return createMmTelFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified subscription.
+     *
+     * @param subscriptionId The subscription ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the subscription or null if the
+     * feature is not supported.
+     */
+    public @Nullable RcsFeature createRcsFeatureForSubscription(int slotId, int subscriptionId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_RCS, true);
+        return createRcsFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new emergency-only {@link MmTelFeature} is
+     * created for the specified slot. For emergency calls, there is no known Subscription Id.
+     *
+     * @param slotId The slot ID that the MMTEL Feature is being created for.
+     * @return An MmTelFeature instance to be used for the slot ID when there is not
+     * subscription inserted. Only requested when there is no subscription active on
+     * the specified slot.
+     */
+    public @Nullable MmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
+        return createMmTelFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
      * specified slot.
+     * @deprecated Use {@link #createMmTelFeatureForSubscription} instead
      *
      * @param slotId The slot ID that the MMTEL Feature is being created for.
      * @return The newly created {@link MmTelFeature} associated with the slot or null if the
      * feature is not supported.
      */
+    @Deprecated
     public MmTelFeature createMmTelFeature(int slotId) {
         return null;
     }
@@ -552,32 +704,62 @@
     /**
      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
      * specified slot.
+     * @deprecated Use {@link #createRcsFeatureForSubscription} instead
      *
      * @param slotId The slot ID that the RCS Feature is being created for.
      * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
      * is not supported.
      */
+    @Deprecated
     public RcsFeature createRcsFeature(int slotId) {
         return null;
     }
 
     /**
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided
+     * subscription. This will be used by the platform to get/set specific IMS related
+     * configurations.
+     *
+     * @param subscriptionId The subscription ID that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified subscription.
+     */
+    public @NonNull ImsConfigImplBase getConfigForSubscription(int slotId, int subscriptionId) {
+        return getConfig(slotId);
+    }
+
+    /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided
+     * subscription.
+     *
+     * @param subscriptionId The subscription ID that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the subscription.
+     */
+    public @NonNull ImsRegistrationImplBase getRegistrationForSubscription(int slotId,
+            int subscriptionId) {
+        return getRegistration(slotId);
+    }
+
+    /**
      * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
      * will be used by the platform to get/set specific IMS related configurations.
+     * @deprecated use {@link #getConfigForSubscription} instead.
      *
      * @param slotId The slot that the IMS configuration is associated with.
      * @return ImsConfig implementation that is associated with the specified slot.
      */
+    @Deprecated
     public ImsConfigImplBase getConfig(int slotId) {
         return new ImsConfigImplBase();
     }
 
     /**
      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+     * @deprecated use  {@link #getRegistrationForSubscription} instead.
      *
      * @param slotId The slot that is associated with the IMS Registration.
      * @return the ImsRegistration implementation associated with the slot.
      */
+    @Deprecated
     public ImsRegistrationImplBase getRegistration(int slotId) {
         return new ImsRegistrationImplBase();
     }
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c6966b3..ae6166f 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -32,18 +32,19 @@
  */
 interface IImsServiceController {
     void setListener(IImsServiceControllerListener l);
-    IImsMmTelFeature createMmTelFeature(int slotId);
-    IImsRcsFeature createRcsFeature(int slotId);
+    IImsMmTelFeature createMmTelFeature(int slotId, int subId);
+    IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId);
+    IImsRcsFeature createRcsFeature(int slotId, int subId);
     ImsFeatureConfiguration querySupportedImsFeatures();
     long getImsServiceCapabilities();
     void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
     // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
     void notifyImsServiceReadyForFeatureCreation();
-    void removeImsFeature(int slotId, int featureType);
-    IImsConfig getConfig(int slotId);
-    IImsRegistration getRegistration(int slotId);
+    void removeImsFeature(int slotId, int featureType, boolean changeSubId);
+    IImsConfig getConfig(int slotId, int subId);
+    IImsRegistration getRegistration(int slotId, int subId);
     ISipTransport getSipTransport(int slotId);
-    oneway void enableIms(int slotId);
-    oneway void disableIms(int slotId);
+    oneway void enableIms(int slotId, int subId);
+    oneway void disableIms(int slotId, int subId);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index f5f67bd..416096b 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -23,11 +23,11 @@
  * {@hide}
  */
 oneway interface IImsServiceFeatureCallback {
-    void imsFeatureCreated(in ImsFeatureContainer feature);
+    void imsFeatureCreated(in ImsFeatureContainer feature, int subId);
     // Reason defined in FeatureConnector.UnavailableReason
     void imsFeatureRemoved(int reason);
     // Status defined in ImsFeature.ImsState.
-    void imsStatusChanged(int status);
+    void imsStatusChanged(int status, int subId);
     //Capabilities defined in ImsService.ImsServiceCapability
     void updateCapabilities(long capabilities);
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ba95841..39ab7eb 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -592,6 +592,7 @@
     int RIL_UNSOL_UNTHROTTLE_APN = 1052;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
+    int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
 
     /* The following unsols are not defined in RIL.h */
     int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index 6985702..9a88abd 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -81,7 +81,8 @@
         intent.setPackage(getPackageName());
         IntentFilter filter = new IntentFilter();
         filter.addAction(intent.getAction());
-        registerReceiver(mAlarmReceiver, filter);
+        registerReceiver(mAlarmReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
         mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 56879c9..c87d8e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -31,7 +31,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
@@ -39,7 +38,6 @@
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -157,11 +155,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c28466c..f2696d8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,12 +34,10 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -117,11 +115,7 @@
 
     @Presubmit
     @Test
-    fun entireScreenCovered() {
-        // This test doesn't work in shell transitions because of b/206086894
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.entireScreenCovered()
-    }
+    fun entireScreenCovered() = testSpec.entireScreenCovered()
 
     @Presubmit
     @Test
@@ -159,11 +153,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index c7f1b99..24b1598 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,7 +32,6 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -134,11 +133,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 46ed0ad..e5d82a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,11 +34,9 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -126,11 +124,7 @@
 
     @Presubmit
     @Test
-    fun entireScreenCovered() {
-        // This test doesn't work in shell transitions because of b/206086894
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.entireScreenCovered()
-    }
+    fun entireScreenCovered() = testSpec.entireScreenCovered()
 
     @Presubmit
     @Test
@@ -152,11 +146,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index ebe4be2..87f8ef2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -34,11 +34,9 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -130,11 +128,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index f6e5adc..0ad0a03 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -101,8 +101,6 @@
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-        // This test doesn't work in shell transitions because of b/204570898
-        assumeFalse(isShellTransitionsEnabled)
         val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertWm {
             this.visibleWindowsShownMoreThanOneConsecutiveEntry(
@@ -116,8 +114,6 @@
     @Presubmit
     @Test
     fun launcherWindowBecomesInvisible() {
-        // This test doesn't work in shell transitions because of b/204574221
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.isAppWindowVisible(LAUNCHER_COMPONENT)
                     .then()
@@ -127,11 +123,7 @@
 
     @Presubmit
     @Test
-    fun imeWindowIsAlwaysVisible() {
-        // This test doesn't work in shell transitions because of b/204570898
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
-    }
+    fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
 
     @Presubmit
     @Test
@@ -202,8 +194,6 @@
     @Presubmit
     @Test
     fun appLayerReplacesLauncher() {
-        // This test doesn't work in shell transitions because of b/204574221
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.isVisible(LAUNCHER_COMPONENT)
                 .then()
@@ -219,11 +209,7 @@
 
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index dcb5c86..8f2803e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -261,7 +261,7 @@
         testSpec.assertWm {
             this.isAppWindowOnTop(LAUNCHER_COMPONENT)
                     .then()
-                    .isAppWindowVisible(FlickerComponentName.SNAPSHOT)
+                    .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
                     .then()
                     .isAppWindowVisible(testApp.component)
         }
@@ -342,4 +342,4 @@
                     )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 0879b98..3f0de7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,13 +25,11 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -167,11 +165,7 @@
      */
     @FlakyTest(bugId = 206753786)
     @Test
-    fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        testSpec.statusBarLayerRotatesScales()
-    }
+    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
     /** {@inheritDoc} */
     @FlakyTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index e44bee6..3ae484b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -26,10 +26,8 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -102,8 +100,6 @@
     @Presubmit
     @Test
     fun appWindowFullScreen() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.invoke("isFullScreen") {
                 val appWindow = it.windowState(testApp.`package`)
@@ -139,8 +135,6 @@
     @Presubmit
     @Test
     fun appLayerAlwaysVisible() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             isVisible(testApp.component)
         }
@@ -152,8 +146,6 @@
     @Presubmit
     @Test
     fun appLayerRotates() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.invoke("entireScreenCovered") { entry ->
                 entry.entry.displays.map { display ->
@@ -193,8 +185,6 @@
     @Presubmit
     @Test
     fun focusDoesNotChange() {
-        // This test doesn't work in shell transitions because of b/206101151
-        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertEventLog {
             this.focusDoesNotChange()
         }
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 4a724b7..2fbcf9d 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -18,25 +18,29 @@
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 import org.junit.Test;
 
 import java.util.HashSet;
 import java.util.Set;
 
-public class VcnCellUnderlyingNetworkTemplateTest {
+public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
     private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
     private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
 
     // Package private for use in VcnGatewayConnectionConfigTest
     static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
         return new VcnCellUnderlyingNetworkTemplate.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
                 .setMetered(MATCH_FORBIDDEN)
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                 .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
                 .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
                 .setRoaming(MATCH_FORBIDDEN)
@@ -47,8 +51,19 @@
     @Test
     public void testBuilderAndGetters() {
         final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
-        assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
         assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitDownstreamBandwidthKbps());
         assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
         assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
         assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
@@ -59,8 +74,14 @@
     public void testBuilderAndGettersForDefaultValues() {
         final VcnCellUnderlyingNetworkTemplate networkPriority =
                 new VcnCellUnderlyingNetworkTemplate.Builder().build();
-        assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
         assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+        // Explicitly expect 0, as documented in Javadoc on setter methods.
+        assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
         assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
         assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
         assertEquals(MATCH_ANY, networkPriority.getRoaming());
@@ -68,6 +89,29 @@
     }
 
     @Test
+    public void testBuilderRequiresStricterEntryCriteria() {
+        try {
+            new VcnCellUnderlyingNetworkTemplate.Builder()
+                    .setMinUpstreamBandwidthKbps(
+                            TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            new VcnCellUnderlyingNetworkTemplate.Builder()
+                    .setMinDownstreamBandwidthKbps(
+                            TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testPersistableBundle() {
         final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
         assertEquals(
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
new file mode 100644
index 0000000..399e136
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+public class VcnUnderlyingNetworkTemplateTestBase {
+    // Public for use in NetworkPriorityClassifierTest
+    public static final int TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS = 200;
+    public static final int TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS = 100;
+    public static final int TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS = 400;
+    public static final int TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS = 300;
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index cb5b47b..4063178 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -17,8 +17,6 @@
 
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -28,15 +26,19 @@
 
 import java.util.Set;
 
-public class VcnWifiUnderlyingNetworkTemplateTest {
+public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
     private static final String SSID = "TestWifi";
-    private static final int INVALID_NETWORK_QUALITY = -1;
 
     // Package private for use in VcnGatewayConnectionConfigTest
     static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
         return new VcnWifiUnderlyingNetworkTemplate.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
                 .setMetered(MATCH_FORBIDDEN)
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                 .setSsids(Set.of(SSID))
                 .build();
     }
@@ -44,8 +46,19 @@
     @Test
     public void testBuilderAndGetters() {
         final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
-        assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
         assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitDownstreamBandwidthKbps());
         assertEquals(Set.of(SSID), networkPriority.getSsids());
     }
 
@@ -53,18 +66,37 @@
     public void testBuilderAndGettersForDefaultValues() {
         final VcnWifiUnderlyingNetworkTemplate networkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder().build();
-        assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
         assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+        // Explicitly expect 0, as documented in Javadoc on setter methods..
+        assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
         assertTrue(networkPriority.getSsids().isEmpty());
     }
 
     @Test
-    public void testBuildWithInvalidNetworkQuality() {
+    public void testBuilderRequiresStricterEntryCriteria() {
         try {
             new VcnWifiUnderlyingNetworkTemplate.Builder()
-                    .setNetworkQuality(INVALID_NETWORK_QUALITY);
-            fail("Expected to fail due to the invalid network quality");
-        } catch (Exception expected) {
+                    .setMinUpstreamBandwidthKbps(
+                            TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            new VcnWifiUnderlyingNetworkTemplate.Builder()
+                    .setMinDownstreamBandwidthKbps(
+                            TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
         }
     }
 
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 4bb7de8..6c849b5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -18,7 +18,10 @@
 
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
 
 import static com.android.server.vcn.VcnTestUtils.setupSystemService;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
@@ -76,6 +79,12 @@
     private static final int CARRIER_ID = 1;
     private static final int CARRIER_ID_OTHER = 2;
 
+    private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+    private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+    private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+    private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
     private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
 
     private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
@@ -83,6 +92,8 @@
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                     .setSignalStrength(WIFI_RSSI)
                     .setSsid(SSID)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
                     .build();
 
     private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
@@ -93,6 +104,8 @@
                     .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                     .setSubscriptionIds(Set.of(SUB_ID))
                     .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
                     .build();
 
     private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
@@ -146,7 +159,6 @@
     public void testMatchWithoutNotMeteredBit() {
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .setMetered(MATCH_FORBIDDEN)
                         .build();
 
@@ -161,11 +173,133 @@
                         null /* carrierConfig */));
     }
 
+    private void verifyMatchesPriorityRuleForUpstreamBandwidth(
+            int entryUpstreamBandwidth,
+            int exitUpstreamBandwidth,
+            UnderlyingNetworkRecord currentlySelected,
+            boolean expectMatch) {
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinUpstreamBandwidthKbps(entryUpstreamBandwidth, exitUpstreamBandwidth)
+                        .build();
+
+        assertEquals(
+                expectMatch,
+                checkMatchesPriorityRule(
+                        mVcnContext,
+                        wifiNetworkPriority,
+                        mWifiNetworkRecord,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        currentlySelected,
+                        null /* carrierConfig */));
+    }
+
+    private void verifyMatchesPriorityRuleForDownstreamBandwidth(
+            int entryDownstreamBandwidth,
+            int exitDownstreamBandwidth,
+            UnderlyingNetworkRecord currentlySelected,
+            boolean expectMatch) {
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinDownstreamBandwidthKbps(
+                                entryDownstreamBandwidth, exitDownstreamBandwidth)
+                        .build();
+
+        assertEquals(
+                expectMatch,
+                checkMatchesPriorityRule(
+                        mVcnContext,
+                        wifiNetworkPriority,
+                        mWifiNetworkRecord,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        currentlySelected,
+                        null /* carrierConfig */));
+    }
+
+    @Test
+    public void testMatchWithEntryUpstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                null /* currentlySelected */,
+                true);
+    }
+
+    @Test
+    public void testMatchWithEntryUpstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                null /* currentlySelected */,
+                false);
+    }
+
+    @Test
+    public void testMatchWithEntryDownstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                null /* currentlySelected */,
+                true);
+    }
+
+    @Test
+    public void testMatchWithEntryDownstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                null /* currentlySelected */,
+                false);
+    }
+
+    @Test
+    public void testMatchWithExitUpstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                mWifiNetworkRecord,
+                true);
+    }
+
+    @Test
+    public void testMatchWithExitUpstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                mWifiNetworkRecord,
+                false);
+    }
+
+    @Test
+    public void testMatchWithExitDownstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                mWifiNetworkRecord,
+                true);
+    }
+
+    @Test
+    public void testMatchWithExitDownstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                mWifiNetworkRecord,
+                false);
+    }
+
     private void verifyMatchWifi(
             boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
+                        .setMinUpstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                        .setMinDownstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                         .build();
         final UnderlyingNetworkRecord selectedNetworkRecord =
                 isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -214,7 +348,12 @@
         final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
+                        .setMinUpstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                        .setMinDownstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                         .setSsids(Set.of(nwPrioritySsid))
                         .build();
 
@@ -238,7 +377,13 @@
     }
 
     private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
-        return new VcnCellUnderlyingNetworkTemplate.Builder().setNetworkQuality(NETWORK_QUALITY_OK);
+        return new VcnCellUnderlyingNetworkTemplate.Builder()
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS);
     }
 
     @Test
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 36bea57..95b43cd 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
 
-if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then
+if git log -n 1 --format='%D' HEAD@{upstream} | grep -q aosp/; then
     # Change appears to be in AOSP
     exit 0
 elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index 9d773e4..ec2bffd 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -14,7 +14,7 @@
         "src/**/*.kt",
     ],
     static_libs: [
-        "asm-6.0",
+        "asm-7.0",
     ],
 }
 
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 22e8d78..0fb062f 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -39,7 +39,7 @@
         kotlin.system.exitProcess(2)
     }
 
-    val ancestorCollector = AncestorCollector(Opcodes.ASM6, null)
+    val ancestorCollector = AncestorCollector(Opcodes.ASM7, null)
 
     for (entry in zipFile.entries()) {
         if (entry.name.endsWith(".class")) {