Merge "Unhide Descrambler class"
diff --git a/Android.bp b/Android.bp
index bf901ec..eef9f80 100644
--- a/Android.bp
+++ b/Android.bp
@@ -265,6 +265,7 @@
         ":framework-sdkext-sources",
         ":framework-statsd-sources",
         ":updatable-media-srcs",
+        ":framework-mediaprovider-sources",
         ":framework-wifi-updatable-sources",
     ]
 }
@@ -380,6 +381,7 @@
         "ext",
         "unsupportedappusage",
         "updatable_media_stubs",
+        "framework_mediaprovider_stubs",
     ],
 
     jarjar_rules: ":framework-jarjar-rules",
@@ -468,6 +470,7 @@
     static_libs: [
         "framework-minus-apex",
         "updatable_media_stubs",
+        "framework_mediaprovider_stubs",
         "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
         "framework-sdkext-stubs-systemapi",
         // TODO(b/146167933): Use framework-statsd-stubs instead.
@@ -583,11 +586,14 @@
 filegroup {
     name: "framework-annotations",
     srcs: [
-        "core/java/android/annotation/NonNull.java",
-        "core/java/android/annotation/Nullable.java",
         "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/IntRange.java",
+        "core/java/android/annotation/NonNull.java",
+        "core/java/android/annotation/Nullable.java",
+        "core/java/android/annotation/RequiresPermission.java",
+        "core/java/android/annotation/SdkConstant.java",
         "core/java/android/annotation/SystemApi.java",
+        "core/java/android/annotation/TestApi.java",
         "core/java/android/annotation/UnsupportedAppUsage.java",
         "core/java/com/android/internal/annotations/GuardedBy.java",
         "core/java/com/android/internal/annotations/VisibleForTesting.java",
@@ -1648,20 +1654,23 @@
 filegroup {
     name: "framework-media-annotation-srcs",
     srcs: [
+        ":framework-annotations",
         "core/java/android/annotation/CallbackExecutor.java",
         "core/java/android/annotation/CallSuper.java",
         "core/java/android/annotation/DrawableRes.java",
-        "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/LongDef.java",
-        "core/java/android/annotation/NonNull.java",
-        "core/java/android/annotation/Nullable.java",
-        "core/java/android/annotation/RequiresPermission.java",
-        "core/java/android/annotation/SdkConstant.java",
         "core/java/android/annotation/StringDef.java",
-        "core/java/android/annotation/SystemApi.java",
-        "core/java/android/annotation/TestApi.java",
-        "core/java/android/annotation/UnsupportedAppUsage.java",
-        "core/java/com/android/internal/annotations/GuardedBy.java",
+    ],
+}
+
+filegroup {
+    name: "framework-mediaprovider-annotation-sources",
+    srcs: [
+        ":framework-annotations",
+        "core/java/android/annotation/BytesLong.java",
+        "core/java/android/annotation/CurrentTimeMillisLong.java",
+        "core/java/android/annotation/CurrentTimeSecondsLong.java",
+        "core/java/android/annotation/DurationMillisLong.java",
     ],
 }
 
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 278a786..661f32f 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -146,7 +146,8 @@
 
             final CountDownLatch latch = new CountDownLatch(1);
             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
-            // Don't use this.startUserInBackground() since only waiting until ACTION_USER_STARTED.
+            // Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until
+            // ACTION_USER_STARTED.
             mIam.startUserInBackground(userId);
             latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
 
@@ -156,6 +157,48 @@
         }
     }
 
+    /**
+     * Measures the time until ACTION_USER_STARTED is received.
+     */
+    @Test
+    public void startUser() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createUserNoFlags();
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
+            mRunner.resumeTiming();
+
+            mIam.startUserInBackground(userId);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+
+            mRunner.pauseTiming();
+            removeUser(userId);
+            mRunner.resumeTiming();
+        }
+    }
+
+    /**
+     * Measures the time until unlock listener is triggered and user is unlocked.
+     */
+    @Test
+    public void startAndUnlockUser() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int userId = createUserNoFlags();
+            mRunner.resumeTiming();
+
+            // Waits for UserState.mUnlockProgress.finish().
+            startUserInBackgroundAndWaitForUnlock(userId);
+
+            mRunner.pauseTiming();
+            removeUser(userId);
+            mRunner.resumeTiming();
+        }
+    }
+
+
+
     @Test
     public void switchUser() throws Exception {
         while (mRunner.keepRunning()) {
@@ -309,7 +352,7 @@
             final int userId = createManagedProfile();
             mRunner.resumeTiming();
 
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
 
             mRunner.pauseTiming();
             removeUser(userId);
@@ -326,11 +369,11 @@
             mRunner.pauseTiming();
             final int userId = createManagedProfile();
             // Start the profile initially, then stop it. Similar to setQuietModeEnabled.
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
             stopUser(userId, true);
             mRunner.resumeTiming();
 
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
 
             mRunner.pauseTiming();
             removeUser(userId);
@@ -352,7 +395,7 @@
             installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
             mRunner.resumeTiming();
 
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
             startApp(userId, DUMMY_PACKAGE_NAME);
 
             mRunner.pauseTiming();
@@ -376,13 +419,13 @@
             final int userId = createManagedProfile();
             WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
             installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
             startApp(userId, DUMMY_PACKAGE_NAME);
             stopUser(userId, true);
             TimeUnit.SECONDS.sleep(1); // Brief cool-down before re-starting profile.
             mRunner.resumeTiming();
 
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
             startApp(userId, DUMMY_PACKAGE_NAME);
 
             mRunner.pauseTiming();
@@ -423,7 +466,7 @@
             mRunner.resumeTiming();
 
             final int userId = createManagedProfile();
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
             installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
             startApp(userId, DUMMY_PACKAGE_NAME);
 
@@ -441,7 +484,7 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int userId = createManagedProfile();
-            startUserInBackground(userId);
+            startUserInBackgroundAndWaitForUnlock(userId);
             mRunner.resumeTiming();
 
             stopUser(userId, true);
@@ -467,7 +510,7 @@
                 final int userId = createManagedProfile();
                 mRunner.resumeTiming();
 
-                startUserInBackground(userId);
+                startUserInBackgroundAndWaitForUnlock(userId);
 
                 mRunner.pauseTiming();
                 removeUser(userId);
@@ -490,7 +533,7 @@
                 final int userId = createManagedProfile();
                 mRunner.resumeTiming();
 
-                startUserInBackground(userId);
+                startUserInBackgroundAndWaitForUnlock(userId);
 
                 mRunner.pauseTiming();
                 removeUser(userId);
@@ -526,18 +569,19 @@
     }
 
     /**
-     * Start user in background and wait for it to unlock (equivalent to ACTION_USER_UNLOCKED).
-     * To start in foreground instead, see {@link #switchUser(int)}.
-     * This should always be used for profiles since profiles cannot be started in foreground.
+     * Start user in background and wait for it to unlock by waiting for
+     * UserState.mUnlockProgress.finish().
+     * <p> To start in foreground instead, see {@link #switchUser(int)}.
+     * <p> This should always be used for profiles since profiles cannot be started in foreground.
      */
-    private void startUserInBackground(int userId) {
+    private void startUserInBackgroundAndWaitForUnlock(int userId) {
         final ProgressWaiter waiter = new ProgressWaiter();
         try {
             mIam.startUserInBackgroundWithListener(userId, waiter);
             boolean success = waiter.waitForFinish(TIMEOUT_IN_SECOND);
             attestTrue("Failed to start user " + userId + " in background.", success);
         } catch (RemoteException e) {
-            Log.e(TAG, "startUserInBackground failed", e);
+            Log.e(TAG, "startUserInBackgroundAndWaitForUnlock failed", e);
         }
     }
 
diff --git a/api/current.txt b/api/current.txt
index bc2a933..6014e1f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -99,6 +99,7 @@
     field public static final String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
     field public static final String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
     field public static final String NFC = "android.permission.NFC";
+    field public static final String NFC_PREFERRED_PAYMENT_INFO = "android.permission.NFC_PREFERRED_PAYMENT_INFO";
     field public static final String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
     field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
     field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
@@ -4308,7 +4309,7 @@
     method public static String permissionToOp(String);
     method public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector);
     method @Deprecated public int startOp(@NonNull String, int, @NonNull String);
-    method public int startOp(@NonNull String, int, @Nullable String, @NonNull String, @Nullable String);
+    method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
     method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
     method public int startOpNoThrow(@NonNull String, int, @NonNull String, @NonNull String, @Nullable String);
     method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
@@ -30417,8 +30418,11 @@
     method public int getMaxMatchFilterLength();
     method public int getMaxServiceNameLength();
     method public int getMaxServiceSpecificInfoLength();
+    method public int getSupportedCipherSuites();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR;
+    field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1
+    field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_256 = 2; // 0x2
   }
 
   public class DiscoverySession implements java.lang.AutoCloseable {
@@ -31130,6 +31134,7 @@
     method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
     field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
+    field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
     field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
     field public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
     field @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT) public static final String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED";
@@ -31138,6 +31143,7 @@
     field public static final String EXTRA_DATA = "android.nfc.extra.DATA";
     field public static final String EXTRA_ID = "android.nfc.extra.ID";
     field public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
+    field public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON = "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON";
     field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
     field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final String EXTRA_TAG = "android.nfc.extra.TAG";
@@ -31148,6 +31154,9 @@
     field public static final int FLAG_READER_NFC_V = 8; // 0x8
     field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100
     field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80
+    field public static final int PREFERRED_PAYMENT_CHANGED = 2; // 0x2
+    field public static final int PREFERRED_PAYMENT_LOADED = 1; // 0x1
+    field public static final int PREFERRED_PAYMENT_UPDATED = 3; // 0x3
     field public static final int STATE_OFF = 1; // 0x1
     field public static final int STATE_ON = 3; // 0x3
     field public static final int STATE_TURNING_OFF = 4; // 0x4
@@ -31203,8 +31212,11 @@
 
   public final class CardEmulation {
     method public boolean categoryAllowsForegroundPreference(String);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService();
     method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getDescriptionForPreferredPaymentService();
     method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService();
     method public int getSelectionModeForCategory(String);
     method public boolean isDefaultServiceForAid(android.content.ComponentName, String);
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
@@ -35442,11 +35454,12 @@
     method public boolean isIgnoringBatteryOptimizations(String);
     method public boolean isInteractive();
     method public boolean isPowerSaveMode();
+    method public boolean isRebootingUserspaceSupported();
     method @Deprecated public boolean isScreenOn();
     method public boolean isSustainedPerformanceModeSupported();
     method public boolean isWakeLockLevelSupported(int);
     method public android.os.PowerManager.WakeLock newWakeLock(int, String);
-    method public void reboot(String);
+    method public void reboot(@Nullable String);
     method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
@@ -39060,6 +39073,8 @@
     field public static final String GENRE = "genre";
     field public static final String HEIGHT = "height";
     field public static final String INSTANCE_ID = "instance_id";
+    field public static final String IS_DOWNLOAD = "is_download";
+    field public static final String IS_DRM = "is_drm";
     field public static final String IS_FAVORITE = "is_favorite";
     field public static final String IS_PENDING = "is_pending";
     field public static final String IS_TRASHED = "is_trashed";
@@ -44839,6 +44854,11 @@
     field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
 
+  public static final class CarrierConfigManager.Gps {
+    field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
+    field public static final String KEY_PREFIX = "gps.";
+  }
+
   public static final class CarrierConfigManager.Ims {
     field public static final String KEY_PREFIX = "ims.";
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index a250eb2..1bca7c9 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -475,15 +475,20 @@
 
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
-    method public long getDuration();
+    method @Deprecated public long getDuration();
     method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
     method public long getLastAccessBackgroundTime(int);
     method public long getLastAccessForegroundTime(int);
     method public long getLastAccessTime(int);
     method public long getLastAccessTime(int, int, int);
     method public long getLastBackgroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+    method public long getLastDuration(int);
     method public long getLastDuration(int, int, int);
     method public long getLastForegroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
     method public long getLastRejectBackgroundTime(int);
     method public long getLastRejectForegroundTime(int);
     method public long getLastRejectTime(int);
@@ -491,34 +496,44 @@
     method public int getMode();
     method @NonNull public String getOpStr();
     method @Deprecated @Nullable public String getProxyPackageName();
-    method @Nullable public String getProxyPackageName(int, int);
+    method @Deprecated @Nullable public String getProxyPackageName(int, int);
     method @Deprecated public int getProxyUid();
-    method public int getProxyUid(int, int);
+    method @Deprecated public int getProxyUid(int, int);
     method public boolean isRunning();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
   }
 
-  public static final class AppOpsManager.OpFeatureEntry {
-    method public long getDuration();
+  public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getFeatureId();
+    method @Nullable public String getPackageName();
+    method @IntRange(from=0) public int getUid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
+  }
+
+  public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable {
+    method public int describeContents();
     method public long getLastAccessBackgroundTime(int);
     method public long getLastAccessForegroundTime(int);
     method public long getLastAccessTime(int);
     method public long getLastAccessTime(int, int, int);
     method public long getLastBackgroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+    method public long getLastDuration(int);
     method public long getLastDuration(int, int, int);
     method public long getLastForegroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
     method public long getLastRejectBackgroundTime(int);
     method public long getLastRejectForegroundTime(int);
     method public long getLastRejectTime(int);
     method public long getLastRejectTime(int, int, int);
-    method @Nullable public String getProxyFeatureId();
-    method @Nullable public String getProxyFeatureId(int, int);
-    method @Nullable public String getProxyPackageName();
-    method @Nullable public String getProxyPackageName(int, int);
-    method public int getProxyUid();
-    method public int getProxyUid(int, int);
     method public boolean isRunning();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR;
   }
 
   public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
@@ -526,7 +541,7 @@
     method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
     method @NonNull public String getPackageName();
     method public int getUid();
-    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
   }
 
@@ -1917,6 +1932,13 @@
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) public void startActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
   }
 
+  public class DataLoaderParams {
+    ctor public DataLoaderParams(@NonNull String, @NonNull String, @Nullable java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>);
+    method @NonNull public final java.util.Map<java.lang.String,android.os.ParcelFileDescriptor> getDynamicArgs();
+    method @NonNull public final String getPackageName();
+    method @NonNull public final String getStaticArgs();
+  }
+
   public final class InstantAppInfo implements android.os.Parcelable {
     ctor public InstantAppInfo(android.content.pm.ApplicationInfo, String[], String[]);
     ctor public InstantAppInfo(String, CharSequence, String[], String[]);
@@ -2006,6 +2028,7 @@
   }
 
   public static class PackageInstaller.Session implements java.io.Closeable {
+    method public void addFile(@NonNull String, long, @NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
   }
 
@@ -2019,6 +2042,7 @@
     method public boolean getInstallAsInstantApp(boolean);
     method public boolean getInstallAsVirtualPreload();
     method public boolean getRequestDowngrade();
+    method public int getRollbackDataPolicy();
     method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
   }
 
@@ -2027,7 +2051,9 @@
     method @Deprecated public void setAllowDowngrade(boolean);
     method public void setDontKillApp(boolean);
     method public void setEnableRollback(boolean);
+    method public void setEnableRollback(boolean, int);
     method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setIncrementalParams(@NonNull android.content.pm.DataLoaderParams);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
     method public void setInstallAsInstantApp(boolean);
     method public void setInstallAsVirtualPreload();
@@ -2178,6 +2204,7 @@
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int FLAG_REMOVED = 2; // 0x2
     field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
+    field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000
     field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
@@ -4018,6 +4045,7 @@
     field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
     field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+    field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb
     field public static final int SUCCESS = 0; // 0x0
   }
 
@@ -6229,6 +6257,130 @@
 
 }
 
+package android.net.wifi.wificond {
+
+  public final class NativeScanResult implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public byte[] getBssid();
+    method @NonNull public java.util.BitSet getCapabilities();
+    method public int getFrequencyMhz();
+    method @NonNull public byte[] getInformationElements();
+    method @NonNull public java.util.List<android.net.wifi.wificond.RadioChainInfo> getRadioChainInfos();
+    method public int getSignalMbm();
+    method @NonNull public byte[] getSsid();
+    method public long getTsf();
+    method public boolean isAssociated();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeScanResult> CREATOR;
+  }
+
+  public final class NativeWifiClient implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeWifiClient> CREATOR;
+    field @NonNull public final byte[] macAddress;
+  }
+
+  public final class PnoNetwork implements android.os.Parcelable {
+    ctor public PnoNetwork();
+    method public int describeContents();
+    method @NonNull public int[] getFrequenciesMhz();
+    method @NonNull public byte[] getSsid();
+    method public boolean isHidden();
+    method public void setFrequenciesMhz(@NonNull int[]);
+    method public void setHidden(boolean);
+    method public void setSsid(@NonNull byte[]);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.PnoNetwork> CREATOR;
+  }
+
+  public final class PnoSettings implements android.os.Parcelable {
+    ctor public PnoSettings();
+    method public int describeContents();
+    method public int getIntervalMillis();
+    method public int getMin2gRssiDbm();
+    method public int getMin5gRssiDbm();
+    method public int getMin6gRssiDbm();
+    method @NonNull public java.util.List<android.net.wifi.wificond.PnoNetwork> getPnoNetworks();
+    method public void setIntervalMillis(int);
+    method public void setMin2gRssiDbm(int);
+    method public void setMin5gRssiDbm(int);
+    method public void setMin6gRssiDbm(int);
+    method public void setPnoNetworks(@NonNull java.util.List<android.net.wifi.wificond.PnoNetwork>);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.PnoSettings> CREATOR;
+  }
+
+  public final class RadioChainInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getChainId();
+    method public int getLevelDbm();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.RadioChainInfo> CREATOR;
+  }
+
+  public class WifiCondManager {
+    method public void abortScan(@NonNull String);
+    method public void enableVerboseLogging(boolean);
+    method @NonNull public int[] getChannelsMhzForBand(int);
+    method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
+    method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
+    method public boolean initialize(@NonNull Runnable);
+    method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback);
+    method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback);
+    method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback);
+    method public boolean setupInterfaceForSoftApMode(@NonNull String);
+    method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String);
+    method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.PnoScanRequestCallback);
+    method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
+    method public boolean stopPnoScan(@NonNull String);
+    method public boolean tearDownClientInterface(@NonNull String);
+    method public boolean tearDownInterfaces();
+    method public boolean tearDownSoftApInterface(@NonNull String);
+    field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1
+    field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0
+    field public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5; // 0x5
+    field public static final int SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED = 2; // 0x2
+    field public static final int SEND_MGMT_FRAME_ERROR_NO_ACK = 3; // 0x3
+    field public static final int SEND_MGMT_FRAME_ERROR_TIMEOUT = 4; // 0x4
+    field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
+  }
+
+  public static interface WifiCondManager.PnoScanRequestCallback {
+    method public void onPnoRequestFailed();
+    method public void onPnoRequestSucceeded();
+  }
+
+  public static interface WifiCondManager.ScanEventCallback {
+    method public void onScanFailed();
+    method public void onScanResultReady();
+  }
+
+  public static interface WifiCondManager.SendMgmtFrameCallback {
+    method public void onAck(int);
+    method public void onFailure(int);
+  }
+
+  public static class WifiCondManager.SignalPollResult {
+    field public final int associationFrequencyMHz;
+    field public final int currentRssiDbm;
+    field public final int rxBitrateMbps;
+    field public final int txBitrateMbps;
+  }
+
+  public static interface WifiCondManager.SoftApCallback {
+    method public void onConnectedClientsChanged(@NonNull android.net.wifi.wificond.NativeWifiClient, boolean);
+    method public void onFailure();
+    method public void onSoftApChannelSwitched(int, int);
+  }
+
+  public static class WifiCondManager.TxPacketCounters {
+    field public final int txPacketFailed;
+    field public final int txPacketSucceeded;
+  }
+
+}
+
 package android.nfc {
 
   public final class NfcAdapter {
@@ -6644,6 +6796,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);
     field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
     field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
+    field public static final String REBOOT_USERSPACE = "userspace";
     field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
     field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
     field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
@@ -7297,8 +7450,12 @@
   }
 
   public final class MediaStore {
+    method @NonNull public static android.net.Uri rewriteToLegacy(@NonNull android.net.Uri);
     method @NonNull public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File);
     method public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String);
+    method public static void waitForIdle(@NonNull android.content.ContentResolver);
+    field public static final String AUTHORITY_LEGACY = "media_legacy";
+    field @NonNull public static final android.net.Uri AUTHORITY_LEGACY_URI;
   }
 
   public abstract class SearchIndexableData {
@@ -7929,6 +8086,14 @@
 
 }
 
+package android.service.dataloader {
+
+  public abstract class DataLoaderService extends android.app.Service {
+    ctor public DataLoaderService();
+  }
+
+}
+
 package android.service.euicc {
 
   public final class DownloadSubscriptionResult implements android.os.Parcelable {
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 9a635757..da0aae0 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -24,6 +24,11 @@
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setWifiP2pChannels(android.net.wifi.p2p.WifiP2pManager.Channel, int, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
     Registration methods should have overload that accepts delivery Executor: `setWifiP2pChannels`
 
+HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities():
+    Type must not be heavy BitSet (method android.net.wifi.wificond.NativeScanResult.getCapabilities())
+PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
+    Found registerApCallback but not unregisterApCallback in android.net.wifi.wificond.WifiCondManager
+
 
 GenericException: android.app.prediction.AppPredictor#finalize():
     
diff --git a/api/test-current.txt b/api/test-current.txt
index d5c7d89..503941c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -158,6 +158,7 @@
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static int getNumOps();
     method public static String[] getOpStrs();
+    method @NonNull @RequiresPermission("android.permission.GET_APP_OPS_STATS") public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
     method public boolean isOperationActive(int, int, String);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void offsetHistory(long);
     method public static int opToDefaultMode(@NonNull String);
@@ -308,15 +309,20 @@
 
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
-    method public long getDuration();
+    method @Deprecated public long getDuration();
     method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
     method public long getLastAccessBackgroundTime(int);
     method public long getLastAccessForegroundTime(int);
     method public long getLastAccessTime(int);
     method public long getLastAccessTime(int, int, int);
     method public long getLastBackgroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+    method public long getLastDuration(int);
     method public long getLastDuration(int, int, int);
     method public long getLastForegroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
     method public long getLastRejectBackgroundTime(int);
     method public long getLastRejectForegroundTime(int);
     method public long getLastRejectTime(int);
@@ -324,34 +330,53 @@
     method public int getMode();
     method @NonNull public String getOpStr();
     method @Deprecated @Nullable public String getProxyPackageName();
-    method @Nullable public String getProxyPackageName(int, int);
+    method @Deprecated @Nullable public String getProxyPackageName(int, int);
     method @Deprecated public int getProxyUid();
-    method public int getProxyUid(int, int);
+    method @Deprecated public int getProxyUid(int, int);
     method public boolean isRunning();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
   }
 
-  public static final class AppOpsManager.OpFeatureEntry {
-    method public long getDuration();
+  public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getFeatureId();
+    method @Nullable public String getPackageName();
+    method @IntRange(from=0) public int getUid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
+  }
+
+  public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable {
+    method public int describeContents();
     method public long getLastAccessBackgroundTime(int);
     method public long getLastAccessForegroundTime(int);
     method public long getLastAccessTime(int);
     method public long getLastAccessTime(int, int, int);
     method public long getLastBackgroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+    method public long getLastDuration(int);
     method public long getLastDuration(int, int, int);
     method public long getLastForegroundDuration(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+    method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
     method public long getLastRejectBackgroundTime(int);
     method public long getLastRejectForegroundTime(int);
     method public long getLastRejectTime(int);
     method public long getLastRejectTime(int, int, int);
-    method @Nullable public String getProxyFeatureId();
-    method @Nullable public String getProxyFeatureId(int, int);
-    method @Nullable public String getProxyPackageName();
-    method @Nullable public String getProxyPackageName(int, int);
-    method public int getProxyUid();
-    method public int getProxyUid(int, int);
     method public boolean isRunning();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR;
+  }
+
+  public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
+    method @NonNull public String getPackageName();
+    method public int getUid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
   }
 
   public class DownloadManager {
@@ -753,11 +778,13 @@
   }
 
   public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
+    method public int getRollbackDataPolicy();
     method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
   }
 
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
     method public void setEnableRollback(boolean);
+    method public void setEnableRollback(boolean, int);
     method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
     method public void setInstallerPackageName(@Nullable String);
@@ -813,6 +840,7 @@
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int FLAG_REMOVED = 2; // 0x2
     field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
+    field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000
     field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index a545fc5..4385964 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -435,6 +435,25 @@
     return eq;
 }
 
+bool subsetDimensions(const std::vector<Matcher>& dimension_a,
+                      const std::vector<Matcher>& dimension_b) {
+    if (dimension_a.size() > dimension_b.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < dimension_a.size(); ++i) {
+        bool found = false;
+        for (size_t j = 0; j < dimension_b.size(); ++j) {
+            if (dimension_a[i] == dimension_b[j]) {
+                found = true;
+            }
+        }
+        if (!found) {
+            return false;
+        }
+    }
+    return true;
+}
+
 bool HasPositionANY(const FieldMatcher& matcher) {
     if (matcher.has_position() && matcher.position() == Position::ANY) {
         return true;
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 0e033e0..6fc1e23 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -396,6 +396,10 @@
 
 bool equalDimensions(const std::vector<Matcher>& dimension_a,
                      const std::vector<Matcher>& dimension_b);
+
+// Returns true if dimension_a is a subset of dimension_b.
+bool subsetDimensions(const std::vector<Matcher>& dimension_a,
+                      const std::vector<Matcher>& dimension_b);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index f1cad92..f4a59ed 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -480,6 +480,137 @@
     EXPECT_EQ(999, atom.num_results());
 }
 
+/*
+ * Test two Matchers is not a subset of one Matcher.
+ * Test one Matcher is subset of two Matchers.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions1) {
+    // Initialize first set of matchers
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+    child->set_position(Position::ALL);
+    child->add_child()->set_field(1);
+    child->add_child()->set_field(2);
+
+    vector<Matcher> matchers1;
+    translateFieldMatcher(matcher1, &matchers1);
+    EXPECT_EQ(2, matchers1.size());
+
+    // Initialize second set of matchers
+    FieldMatcher matcher2;
+    matcher2.set_field(10);
+
+    child = matcher2.add_child();
+    child->set_field(1);
+    child->set_position(Position::ALL);
+    child->add_child()->set_field(1);
+
+    vector<Matcher> matchers2;
+    translateFieldMatcher(matcher2, &matchers2);
+    EXPECT_EQ(1, matchers2.size());
+
+    EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
+    EXPECT_TRUE(subsetDimensions(matchers2, matchers1));
+}
+/*
+ * Test not a subset with one matching Matcher, one non-matching Matcher.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions2) {
+    // Initialize first set of matchers
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+
+    child = matcher1.add_child();
+    child->set_field(2);
+
+    vector<Matcher> matchers1;
+    translateFieldMatcher(matcher1, &matchers1);
+
+    // Initialize second set of matchers
+    FieldMatcher matcher2;
+    matcher2.set_field(10);
+
+    child = matcher2.add_child();
+    child->set_field(1);
+
+    child = matcher2.add_child();
+    child->set_field(3);
+
+    vector<Matcher> matchers2;
+    translateFieldMatcher(matcher2, &matchers2);
+
+    EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
+}
+
+/*
+ * Test not a subset if parent field is not equal.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions3) {
+    // Initialize first set of matchers
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+
+    vector<Matcher> matchers1;
+    translateFieldMatcher(matcher1, &matchers1);
+
+    // Initialize second set of matchers
+    FieldMatcher matcher2;
+    matcher2.set_field(5);
+
+    child = matcher2.add_child();
+    child->set_field(1);
+
+    vector<Matcher> matchers2;
+    translateFieldMatcher(matcher2, &matchers2);
+
+    EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
+}
+
+/*
+ * Test is subset with two matching Matchers.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions4) {
+    // Initialize first set of matchers
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+
+    child = matcher1.add_child();
+    child->set_field(2);
+
+    vector<Matcher> matchers1;
+    translateFieldMatcher(matcher1, &matchers1);
+
+    // Initialize second set of matchers
+    FieldMatcher matcher2;
+    matcher2.set_field(10);
+
+    child = matcher2.add_child();
+    child->set_field(1);
+
+    child = matcher2.add_child();
+    child->set_field(2);
+
+    child = matcher2.add_child();
+    child->set_field(3);
+
+    vector<Matcher> matchers2;
+    translateFieldMatcher(matcher2, &matchers2);
+
+    EXPECT_TRUE(subsetDimensions(matchers1, matchers2));
+    EXPECT_FALSE(subsetDimensions(matchers2, matchers1));
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/core/java/android/app/AppOpsManager.aidl b/core/java/android/app/AppOpsManager.aidl
index 2240302..b4dee2e 100644
--- a/core/java/android/app/AppOpsManager.aidl
+++ b/core/java/android/app/AppOpsManager.aidl
@@ -17,6 +17,9 @@
 package android.app;
 
 parcelable AppOpsManager.PackageOps;
+parcelable AppOpsManager.NoteOpEventProxyInfo;
+parcelable AppOpsManager.NoteOpEvent;
+parcelable AppOpsManager.OpFeatureEntry;
 parcelable AppOpsManager.OpEntry;
 
 parcelable AppOpsManager.HistoricalOp;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ce89754c..86a5c76 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -48,9 +48,9 @@
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
-import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -63,6 +63,8 @@
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.ZygoteInit;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.ElementType;
@@ -83,7 +85,6 @@
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
-import java.util.function.ToLongFunction;
 
 /**
  * API for interacting with "application operation" tracking.
@@ -138,7 +139,7 @@
     @GuardedBy("sLock")
     private static @Nullable AppOpsCollector sNotedAppOpsCollector;
 
-    static IBinder sToken;
+    static IBinder sClientId;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -2259,6 +2260,7 @@
      * Class holding all of the operation information associated with an app.
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final class PackageOps implements Parcelable {
         private final String mPackageName;
@@ -2302,7 +2304,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeString(mPackageName);
             dest.writeInt(mUid);
             dest.writeInt(mEntries.size());
@@ -2333,537 +2335,878 @@
     }
 
     /**
-     * Class holding the information about one unique operation of a
-     * {@link Context#createFeatureContext(String) feature}.
+     * Proxy information for a {@link #noteOp} event
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @Immutable
+    @DataClass(genHiddenConstructor = true)
+    public static final class OpEventProxyInfo implements Parcelable {
+        /** UID of the proxy app that noted the op */
+        private final @IntRange(from = 0) int mUid;
+        /** Package of the proxy that noted the op */
+        private final @Nullable String mPackageName;
+        /** ID of the feature of the proxy that noted the op */
+        private final @Nullable String mFeatureId;
+
+
+
+        // Code below generated by codegen v1.0.14.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
+
+
+        /**
+         * Creates a new OpEventProxyInfo.
+         *
+         * @param uid
+         *   UID of the proxy app that noted the op
+         * @param packageName
+         *   Package of the proxy that noted the op
+         * @param featureId
+         *   ID of the feature of the proxy that noted the op
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public OpEventProxyInfo(
+                @IntRange(from = 0) int uid,
+                @Nullable String packageName,
+                @Nullable String featureId) {
+            this.mUid = uid;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, mUid,
+                    "from", 0);
+            this.mPackageName = packageName;
+            this.mFeatureId = featureId;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        /**
+         * UID of the proxy app that noted the op
+         */
+        @DataClass.Generated.Member
+        public @IntRange(from = 0) int getUid() {
+            return mUid;
+        }
+
+        /**
+         * Package of the proxy that noted the op
+         */
+        @DataClass.Generated.Member
+        public @Nullable String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * ID of the feature of the proxy that noted the op
+         */
+        @DataClass.Generated.Member
+        public @Nullable String getFeatureId() {
+            return mFeatureId;
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            // You can override field parcelling by defining methods like:
+            // void parcelFieldName(Parcel dest, int flags) { ... }
+
+            byte flg = 0;
+            if (mPackageName != null) flg |= 0x2;
+            if (mFeatureId != null) flg |= 0x4;
+            dest.writeByte(flg);
+            dest.writeInt(mUid);
+            if (mPackageName != null) dest.writeString(mPackageName);
+            if (mFeatureId != null) dest.writeString(mFeatureId);
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int describeContents() { return 0; }
+
+        /** @hide */
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        @DataClass.Generated.Member
+        /* package-private */ OpEventProxyInfo(@NonNull Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            byte flg = in.readByte();
+            int uid = in.readInt();
+            String packageName = (flg & 0x2) == 0 ? null : in.readString();
+            String featureId = (flg & 0x4) == 0 ? null : in.readString();
+
+            this.mUid = uid;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, mUid,
+                    "from", 0);
+            this.mPackageName = packageName;
+            this.mFeatureId = featureId;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @DataClass.Generated.Member
+        public static final @NonNull Parcelable.Creator<OpEventProxyInfo> CREATOR
+                = new Parcelable.Creator<OpEventProxyInfo>() {
+            @Override
+            public OpEventProxyInfo[] newArray(int size) {
+                return new OpEventProxyInfo[size];
+            }
+
+            @Override
+            public OpEventProxyInfo createFromParcel(@NonNull Parcel in) {
+                return new OpEventProxyInfo(in);
+            }
+        };
+
+        @DataClass.Generated(
+                time = 1576194071700L,
+                codegenVersion = "1.0.14",
+                sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
+                inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+        @Deprecated
+        private void __metadata() {}
+
+
+        //@formatter:on
+        // End of generated code
+
+    }
+
+    /**
+     * Description of a {@link #noteOp} or {@link #startOp} event
+     *
+     * @hide
+     */
+    @Immutable
+    //@DataClass codegen verifier is broken
+    public static final class NoteOpEvent implements Parcelable {
+        /** Time of noteOp event */
+        public final @IntRange(from = 0) long noteTime;
+        /** The duration of this event (in case this is a startOp event, -1 otherwise). */
+        public final @IntRange(from = -1) long duration;
+        /** Proxy information of the noteOp event */
+        public final @Nullable OpEventProxyInfo proxy;
+
+
+        // Code below generated by codegen v1.0.14.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
+
+
+        /**
+         * Creates a new NoteOpEvent.
+         *
+         * @param noteTime
+         *   Time of noteOp event
+         * @param duration
+         *   The duration of this event (in case this is a startOp event, -1 otherwise).
+         * @param proxy
+         *   Proxy information of the noteOp event
+         */
+        @DataClass.Generated.Member
+        public NoteOpEvent(
+                @IntRange(from = 0) long noteTime,
+                @IntRange(from = -1) long duration,
+                @Nullable OpEventProxyInfo proxy) {
+            this.noteTime = noteTime;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, noteTime,
+                    "from", 0);
+            this.duration = duration;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, duration,
+                    "from", -1);
+            this.proxy = proxy;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            // You can override field parcelling by defining methods like:
+            // void parcelFieldName(Parcel dest, int flags) { ... }
+
+            byte flg = 0;
+            if (proxy != null) flg |= 0x4;
+            dest.writeByte(flg);
+            dest.writeLong(noteTime);
+            dest.writeLong(duration);
+            if (proxy != null) dest.writeTypedObject(proxy, flags);
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int describeContents() { return 0; }
+
+        /** @hide */
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        @DataClass.Generated.Member
+        /* package-private */ NoteOpEvent(@NonNull Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            byte flg = in.readByte();
+            long _noteTime = in.readLong();
+            long _duration = in.readLong();
+            OpEventProxyInfo _proxy = (flg & 0x4) == 0 ? null : (OpEventProxyInfo) in.readTypedObject(
+                    OpEventProxyInfo.CREATOR);
+
+            this.noteTime = _noteTime;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, noteTime,
+                    "from", 0);
+            this.duration = _duration;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, duration,
+                    "from", -1);
+            this.proxy = _proxy;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @DataClass.Generated.Member
+        public static final @NonNull Parcelable.Creator<NoteOpEvent> CREATOR
+                = new Parcelable.Creator<NoteOpEvent>() {
+            @Override
+            public NoteOpEvent[] newArray(int size) {
+                return new NoteOpEvent[size];
+            }
+
+            @Override
+            public NoteOpEvent createFromParcel(@NonNull Parcel in) {
+                return new NoteOpEvent(in);
+            }
+        };
+
+        /*
+        @DataClass.Generated(
+                time = 1574809856220L,
+                codegenVersion = "1.0.14",
+                sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
+                inputSignatures = "public final @android.annotation.IntRange(from=0L) long noteTime\npublic final @android.annotation.IntRange(from=-1) long duration\npublic final @android.annotation.Nullable android.app.NoteOpEventProxyInfo proxy\nclass NoteOpEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
+        @Deprecated
+        private void __metadata() {}
+         */
+
+
+        //@formatter:on
+        // End of generated code
+
+    }
+
+    /**
+     * Last {@link #noteOp} and {@link #startOp} events performed for a single op and a specific
+     * {@link Context#createFeatureContext(String) feature} for all uidModes and opFlags.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @Immutable
+    // @DataClass(genHiddenConstructor = true) codegen verifier is broken
+    @DataClass.Suppress({"getAccessEvents", "getRejectEvents", "getOp"})
+    public static final class OpFeatureEntry implements Parcelable {
+        /** The code of the op */
+        private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
+        /** Whether the op is running */
+        private final boolean mRunning;
+        /** The access events */
+        @DataClass.ParcelWith(LongSparseArrayParceling.class)
+        private final @Nullable LongSparseArray<NoteOpEvent> mAccessEvents;
+        /** The rejection events */
+        @DataClass.ParcelWith(LongSparseArrayParceling.class)
+        private final @Nullable LongSparseArray<NoteOpEvent> mRejectEvents;
+
+        /**
+         * Returns all keys for which we have events.
+         *
+         * @hide
+         */
+        public @NonNull ArraySet<Long> collectKeys() {
+            ArraySet<Long> keys = new ArraySet<>();
+
+            if (mAccessEvents != null) {
+                int numEvents = mAccessEvents.size();
+                for (int i = 0; i < numEvents; i++) {
+                    keys.add(mAccessEvents.keyAt(i));
+                }
+            }
+
+            if (mRejectEvents != null) {
+                int numEvents = mRejectEvents.size();
+                for (int i = 0; i < numEvents; i++) {
+                    keys.add(mRejectEvents.keyAt(i));
+                }
+            }
+
+            return keys;
+        }
+
+        /**
+         * Return the last access time.
+         *
+         * @param flags The op flags
+         *
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastAccessForegroundTime(int)
+         * @see #getLastAccessBackgroundTime(int)
+         * @see #getLastAccessTime(int, int, int)
+         * @see OpEntry#getLastAccessTime(int)
+         */
+        public long getLastAccessTime(@OpFlags int flags) {
+            return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
+        }
+
+        /**
+         * Return the last foreground access time.
+         *
+         * @param flags The op flags
+         *
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastAccessTime(int)
+         * @see #getLastAccessBackgroundTime(int)
+         * @see #getLastAccessTime(int, int, int)
+         * @see OpEntry#getLastAccessForegroundTime(int)
+         */
+        public long getLastAccessForegroundTime(@OpFlags int flags) {
+            return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
+        }
+
+        /**
+         * Return the last background access time.
+         *
+         * @param flags The op flags
+         *
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastAccessTime(int)
+         * @see #getLastAccessForegroundTime(int)
+         * @see #getLastAccessTime(int, int, int)
+         * @see OpEntry#getLastAccessBackgroundTime(int)
+         */
+        public long getLastAccessBackgroundTime(@OpFlags int flags) {
+            return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
+        }
+
+        /**
+         * Return the last access event.
+         *
+         * @param flags The op flags
+         *
+         * @return the last access event of {@code null}
+         */
+        private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
+                @UidState int toUidState, @OpFlags int flags) {
+            return getLastEvent(mAccessEvents, fromUidState, toUidState, flags);
+        }
+
+        /**
+         * Return the last access time.
+         *
+         * @param fromUidState The lowest UID state for which to query
+         * @param toUidState The highest UID state for which to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastAccessTime(int)
+         * @see #getLastAccessForegroundTime(int)
+         * @see #getLastAccessBackgroundTime(int)
+         * @see OpEntry#getLastAccessTime(int, int, int)
+         */
+        public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
+            if (lastEvent == null) {
+                return -1;
+            }
+
+            return lastEvent.noteTime;
+        }
+
+        /**
+         * Return the last rejection time.
+         *
+         * @param flags The op flags
+         *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectForegroundTime(int)
+         * @see #getLastRejectBackgroundTime(int)
+         * @see #getLastRejectTime(int, int, int)
+         * @see OpEntry#getLastRejectTime(int)
+         */
+        public long getLastRejectTime(@OpFlags int flags) {
+            return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
+        }
+
+        /**
+         * Return the last foreground rejection time.
+         *
+         * @param flags The op flags
+         *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
+         * @see #getLastRejectBackgroundTime(int)
+         * @see #getLastRejectTime(int, int, int)
+         * @see OpEntry#getLastRejectForegroundTime(int)
+         */
+        public long getLastRejectForegroundTime(@OpFlags int flags) {
+            return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
+        }
+
+        /**
+         * Return the last background rejection time.
+         *
+         * @param flags The op flags
+         *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
+         * @see #getLastRejectForegroundTime(int)
+         * @see #getLastRejectTime(int, int, int)
+         * @see OpEntry#getLastRejectBackgroundTime(int)
+         */
+        public long getLastRejectBackgroundTime(@OpFlags int flags) {
+            return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
+        }
+
+        /**
+         * Return the last background rejection event.
+         *
+         * @param flags The op flags
+         *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
+         * @see #getLastRejectForegroundTime(int)
+         * @see #getLastRejectBackgroundTime(int)
+         * @see OpEntry#getLastRejectTime(int, int, int)
+         */
+        private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
+                @UidState int toUidState, @OpFlags int flags) {
+            return getLastEvent(mRejectEvents, fromUidState, toUidState, flags);
+        }
+
+        /**
+         * Return the last rejection time.
+         *
+         * @param fromUidState The lowest UID state for which to query
+         * @param toUidState The highest UID state for which to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return the last access time (in milliseconds since epoch) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
+         * @see #getLastRejectForegroundTime(int)
+         * @see #getLastRejectForegroundTime(int)
+         * @see #getLastRejectTime(int, int, int)
+         * @see OpEntry#getLastRejectTime(int, int, int)
+         */
+        public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            NoteOpEvent lastEvent = getLastRejectEvent(fromUidState, toUidState, flags);
+            if (lastEvent == null) {
+                return -1;
+            }
+
+            return lastEvent.noteTime;
+        }
+
+        /**
+         * Return the duration in milliseconds of the last the access.
+         *
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastForegroundDuration(int)
+         * @see #getLastBackgroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         * @see OpEntry#getLastDuration(int)
+         */
+        public long getLastDuration(@OpFlags int flags) {
+            return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
+        }
+
+        /**
+         * Return the duration in milliseconds of the last foreground access.
+         *
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastDuration(int)
+         * @see #getLastBackgroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         * @see OpEntry#getLastForegroundDuration(int)
+         */
+        public long getLastForegroundDuration(@OpFlags int flags) {
+            return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
+        }
+
+        /**
+         * Return the duration in milliseconds of the last background access.
+         *
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastDuration(int)
+         * @see #getLastForegroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         * @see OpEntry#getLastBackgroundDuration(int)
+         */
+        public long getLastBackgroundDuration(@OpFlags int flags) {
+            return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
+        }
+
+        /**
+         * Return the duration in milliseconds of the last access.
+         *
+         * @param fromUidState The lowest UID state for which to query
+         * @param toUidState The highest UID state for which to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastDuration(int)
+         * @see #getLastForegroundDuration(int)
+         * @see #getLastBackgroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         * @see OpEntry#getLastDuration(int, int, int)
+         */
+        public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);;
+            if (lastEvent == null) {
+                return -1;
+            }
+
+            return lastEvent.duration;
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last access on behalf of this feature
+         * and as a result blamed the op on this app.
+         *
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastForegroundProxyInfo(int)
+         * @see #getLastBackgroundProxyInfo(int)
+         * @see #getLastProxyInfo(int, int, int)
+         * @see OpEntry#getLastProxyInfo(int)
+         */
+        public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) {
+            return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last foreground access on behalf of
+         * this feature and as a result blamed the op on this app.
+         *
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastProxyInfo(int)
+         * @see #getLastBackgroundProxyInfo(int)
+         * @see #getLastProxyInfo(int, int, int)
+         * @see OpEntry#getLastForegroundProxyInfo(int)
+         */
+        public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) {
+            return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last background access on behalf of
+         * this feature and as a result blamed the op on this app.
+         *
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastProxyInfo(int)
+         * @see #getLastForegroundProxyInfo(int)
+         * @see #getLastProxyInfo(int, int, int)
+         * @see OpEntry#getLastBackgroundProxyInfo(int)
+         */
+        public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) {
+            return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last access on behalf of this feature
+         * and as a result blamed the op on this app.
+         *
+         * @param fromUidState The lowest UID state for which to query
+         * @param toUidState The highest UID state for which to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastProxyInfo(int)
+         * @see #getLastForegroundProxyInfo(int)
+         * @see #getLastBackgroundProxyInfo(int)
+         * @see OpEntry#getLastProxyInfo(int, int, int)
+         */
+        public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState,
+                @UidState int toUidState, @OpFlags int flags) {
+            NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
+            if (lastEvent == null) {
+                return null;
+            }
+
+            return lastEvent.proxy;
+        }
+
+        private static class LongSparseArrayParceling implements
+                Parcelling<LongSparseArray<NoteOpEvent>> {
+            @Override
+            public void parcel(@Nullable LongSparseArray<NoteOpEvent> array, @NonNull Parcel dest,
+                    int parcelFlags) {
+                if (array == null) {
+                    dest.writeInt(-1);
+                    return;
+                }
+
+                int numEntries = array.size();
+                dest.writeInt(numEntries);
+
+                for (int i = 0; i < numEntries; i++) {
+                    dest.writeLong(array.keyAt(i));
+                    dest.writeParcelable(array.valueAt(i), parcelFlags);
+                }
+            }
+
+            @Override
+            public @Nullable LongSparseArray<NoteOpEvent> unparcel(@NonNull Parcel source) {
+                int numEntries = source.readInt();
+                if (numEntries == -1) {
+                    return null;
+                }
+
+                LongSparseArray<NoteOpEvent> array = new LongSparseArray<>(numEntries);
+
+                for (int i = 0; i < numEntries; i++) {
+                    array.put(source.readLong(), source.readParcelable(null));
+                }
+
+                return array;
+            }
+        }
+
+
+
+        // Code below generated by codegen v1.0.14.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
+
+
+        /**
+         * Creates a new OpFeatureEntry.
+         *
+         * @param op
+         *   The code of the op
+         * @param running
+         *   Whether the op is running
+         * @param accessEvents
+         *   The access events
+         * @param rejectEvents
+         *   The rejection events
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public OpFeatureEntry(
+                @IntRange(from = 0, to = _NUM_OP - 1) int op,
+                boolean running,
+                @Nullable LongSparseArray<NoteOpEvent> accessEvents,
+                @Nullable LongSparseArray<NoteOpEvent> rejectEvents) {
+            this.mOp = op;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, mOp,
+                    "from", 0,
+                    "to", _NUM_OP - 1);
+            this.mRunning = running;
+            this.mAccessEvents = accessEvents;
+            this.mRejectEvents = rejectEvents;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        /**
+         * Whether the op is running
+         */
+        @DataClass.Generated.Member
+        public boolean isRunning() {
+            return mRunning;
+        }
+
+        @DataClass.Generated.Member
+        static Parcelling<LongSparseArray<NoteOpEvent>> sParcellingForAccessEvents =
+                Parcelling.Cache.get(
+                        LongSparseArrayParceling.class);
+        static {
+            if (sParcellingForAccessEvents == null) {
+                sParcellingForAccessEvents = Parcelling.Cache.put(
+                        new LongSparseArrayParceling());
+            }
+        }
+
+        @DataClass.Generated.Member
+        static Parcelling<LongSparseArray<NoteOpEvent>> sParcellingForRejectEvents =
+                Parcelling.Cache.get(
+                        LongSparseArrayParceling.class);
+        static {
+            if (sParcellingForRejectEvents == null) {
+                sParcellingForRejectEvents = Parcelling.Cache.put(
+                        new LongSparseArrayParceling());
+            }
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            // You can override field parcelling by defining methods like:
+            // void parcelFieldName(Parcel dest, int flags) { ... }
+
+            byte flg = 0;
+            if (mRunning) flg |= 0x2;
+            if (mAccessEvents != null) flg |= 0x4;
+            if (mRejectEvents != null) flg |= 0x8;
+            dest.writeByte(flg);
+            dest.writeInt(mOp);
+            sParcellingForAccessEvents.parcel(mAccessEvents, dest, flags);
+            sParcellingForRejectEvents.parcel(mRejectEvents, dest, flags);
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int describeContents() { return 0; }
+
+        /** @hide */
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        @DataClass.Generated.Member
+        /* package-private */ OpFeatureEntry(@NonNull Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            byte flg = in.readByte();
+            boolean running = (flg & 0x2) != 0;
+            int op = in.readInt();
+            LongSparseArray<NoteOpEvent> accessEvents = sParcellingForAccessEvents.unparcel(in);
+            LongSparseArray<NoteOpEvent> rejectEvents = sParcellingForRejectEvents.unparcel(in);
+
+            this.mOp = op;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, mOp,
+                    "from", 0,
+                    "to", _NUM_OP - 1);
+            this.mRunning = running;
+            this.mAccessEvents = accessEvents;
+            this.mRejectEvents = rejectEvents;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @DataClass.Generated.Member
+        public static final @NonNull Parcelable.Creator<OpFeatureEntry> CREATOR
+                = new Parcelable.Creator<OpFeatureEntry>() {
+            @Override
+            public OpFeatureEntry[] newArray(int size) {
+                return new OpFeatureEntry[size];
+            }
+
+            @Override
+            public OpFeatureEntry createFromParcel(@NonNull Parcel in) {
+                return new OpFeatureEntry(in);
+            }
+        };
+
+        /*
+        @DataClass.Generated(
+                time = 1574809856239L,
+                codegenVersion = "1.0.14",
+                sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
+                inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final  boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic  long getAccessTime(int,int)\npublic  long getRejectTime(int,int)\npublic  long getDuration(int,int)\npublic  int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyFeatureId(int,int)\nclass OpFeatureEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+        @Deprecated
+        private void __metadata() {}
+         */
+
+
+        //@formatter:on
+        // End of generated code
+
+    }
+
+    /**
+     * Last {@link #noteOp} and {@link #startOp} events performed for a single op for all uidModes
+     * and opFlags.
      *
      * @hide
      */
     @TestApi
     @Immutable
     @SystemApi
-    public static final class OpFeatureEntry {
-        private final @NonNull OpEntry mParent;
-        private final boolean mRunning;
-
-        private final @Nullable LongSparseLongArray mAccessTimes;
-        private final @Nullable LongSparseLongArray mRejectTimes;
-        private final @Nullable LongSparseLongArray mDurations;
-        private final @Nullable LongSparseLongArray mProxyUids;
-        private final @Nullable LongSparseArray<String> mProxyPackageNames;
-        private final @Nullable LongSparseArray<String> mProxyFeatureIds;
-
-        /**
-         * @hide
-         */
-        public OpFeatureEntry(@NonNull OpEntry parent, boolean running,
-                @Nullable LongSparseLongArray accessTimes,
-                @Nullable LongSparseLongArray rejectTimes,
-                @Nullable LongSparseLongArray durations, @Nullable LongSparseLongArray proxyUids,
-                @Nullable LongSparseArray<String> proxyPackageNames,
-                @Nullable LongSparseArray<String> proxyFeatureIds) {
-            mParent = Preconditions.checkNotNull(parent);
-            mRunning = running;
-            mAccessTimes = accessTimes;
-            mRejectTimes = rejectTimes;
-            mDurations = durations;
-            mProxyUids = proxyUids;
-            mProxyPackageNames = proxyPackageNames;
-            mProxyFeatureIds = proxyFeatureIds;
-        }
-
-        /**
-         * Returns all keys for which we have mapped state in any of the data buckets -
-         * access time, reject time, duration.
-         * @hide */
-        public @Nullable LongSparseArray<Object> collectKeys() {
-            LongSparseArray<Object> result = AppOpsManager.collectKeys(mAccessTimes, null);
-            result = AppOpsManager.collectKeys(mRejectTimes, result);
-            result = AppOpsManager.collectKeys(mDurations, result);
-            return result;
-        }
-
-        /**
-         * @hide
-         */
-        public long getTime() {
-            return getLastAccessTime(OP_FLAGS_ALL);
-        }
-
-        /**
-         * Return the last wall clock time in milliseconds this op was accessed.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastAccessForegroundTime(int)
-         * @see #getLastAccessBackgroundTime(int)
-         * @see #getLastAccessTime(int, int, int)
-         */
-        public long getLastAccessTime(@OpFlags int flags) {
-            return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE,
-                    MIN_PRIORITY_UID_STATE, flags);
-        }
-
-        /**
-         * Return the last wall clock time in milliseconds this op was accessed
-         * by the app while in the foreground.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastAccessBackgroundTime(int)
-         * @see #getLastAccessTime(int)
-         * @see #getLastAccessTime(int, int, int)
-         */
-        public long getLastAccessForegroundTime(@OpFlags int flags) {
-            return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE,
-                    resolveFirstUnrestrictedUidState(mParent.mOp), flags);
-        }
-
-        /**
-         * Return the last wall clock time in milliseconds this op was accessed
-         * by the app while in the background.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastAccessForegroundTime(int)
-         * @see #getLastAccessTime(int)
-         * @see #getLastAccessTime(int, int, int)
-         */
-        public long getLastAccessBackgroundTime(@OpFlags int flags) {
-            return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mParent.mOp),
-                    MIN_PRIORITY_UID_STATE, flags);
-        }
-
-        /**
-         * Return the last wall clock time  in milliseconds this op was accessed
-         * by the app for a given range of UID states.
-         *
-         * @param fromUidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state for which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         *
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastAccessForegroundTime(int)
-         * @see #getLastAccessBackgroundTime(int)
-         * @see #getLastAccessTime(int)
-         */
-        public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
-                @OpFlags int flags) {
-            return maxForFlagsInStates(mAccessTimes, fromUidState, toUidState, flags);
-        }
-
-        /**
-         * @hide
-         */
-        public long getRejectTime() {
-            return getLastRejectTime(OP_FLAGS_ALL);
-        }
-
-        /**
-         * Return the last wall clock time in milliseconds the app made an attempt
-         * to access this op but was rejected.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last reject time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastRejectBackgroundTime(int)
-         * @see #getLastRejectForegroundTime(int)
-         * @see #getLastRejectTime(int, int, int)
-         */
-        public long getLastRejectTime(@OpFlags int flags) {
-            return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE,
-                    MIN_PRIORITY_UID_STATE, flags);
-        }
-
-        /**
-         * Return the last wall clock time in milliseconds the app made an attempt
-         * to access this op while in the foreground but was rejected.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground reject time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastRejectBackgroundTime(int)
-         * @see #getLastRejectTime(int, int, int)
-         * @see #getLastRejectTime(int)
-         */
-        public long getLastRejectForegroundTime(@OpFlags int flags) {
-            return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE,
-                    resolveFirstUnrestrictedUidState(mParent.mOp), flags);
-        }
-
-        /**
-         * Return the last wall clock time in milliseconds the app made an attempt
-         * to access this op while in the background but was rejected.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last background reject time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastRejectForegroundTime(int)
-         * @see #getLastRejectTime(int, int, int)
-         * @see #getLastRejectTime(int)
-         */
-        public long getLastRejectBackgroundTime(@OpFlags int flags) {
-            return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mParent.mOp),
-                    MIN_PRIORITY_UID_STATE, flags);
-        }
-
-        /**
-         * Return the last wall clock time state in milliseconds the app made an
-         * attempt to access this op for a given range of UID states.
-         *
-         * @param fromUidState The UID state from which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state to which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
-         *
-         * @see #getLastRejectForegroundTime(int)
-         * @see #getLastRejectBackgroundTime(int)
-         * @see #getLastRejectTime(int)
-         */
-        public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
-                @OpFlags int flags) {
-            return maxForFlagsInStates(mRejectTimes, fromUidState, toUidState, flags);
-        }
-
-        /**
-         * @return Whether the operation is running.
-         */
-        public boolean isRunning() {
-            return mRunning;
-        }
-
-        /**
-         * @return The duration of the operation in milliseconds. The duration is in wall time.
-         */
-        public long getDuration() {
-            return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
-        }
-
-        /**
-         * Return the duration in milliseconds the app accessed this op while
-         * in the foreground. The duration is in wall time.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the foreground access duration in milliseconds.
-         *
-         * @see #getLastBackgroundDuration(int)
-         * @see #getLastDuration(int, int, int)
-         */
-        public long getLastForegroundDuration(@OpFlags int flags) {
-            return sumForFlagsInStates(mDurations, MAX_PRIORITY_UID_STATE,
-                    resolveFirstUnrestrictedUidState(mParent.mOp), flags);
-        }
-
-        /**
-         * Return the duration in milliseconds the app accessed this op while
-         * in the background. The duration is in wall time.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the background access duration in milliseconds.
-         *
-         * @see #getLastForegroundDuration(int)
-         * @see #getLastDuration(int, int, int)
-         */
-        public long getLastBackgroundDuration(@OpFlags int flags) {
-            return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mParent.mOp),
-                    MIN_PRIORITY_UID_STATE, flags);
-        }
-
-        /**
-         * Return the duration in milliseconds the app accessed this op for
-         * a given range of UID states. The duration is in wall time.
-         *
-         * @param fromUidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state for which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the access duration in milliseconds.
-         */
-        public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
-                @OpFlags int flags) {
-            return sumForFlagsInStates(mDurations, fromUidState, toUidState, flags);
-        }
-
-        /**
-         * Gets the UID of the app that performed the op on behalf of this app and
-         * as a result blamed the op on this app or {@link Process#INVALID_UID} if
-         * there is no proxy.
-         *
-         * @return The proxy UID.
-         */
-        public int getProxyUid() {
-            return (int) findFirstNonNegativeForFlagsInStates(mProxyUids,
-                    MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
-        }
-
-        /**
-         * Gets the UID of the app that performed the op on behalf of this app and
-         * as a result blamed the op on this app or {@link Process#INVALID_UID} if
-         * there is no proxy.
-         *
-         * @param uidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         *
-         * @return The proxy UID.
-         */
-        public int getProxyUid(@UidState int uidState, @OpFlags int flags) {
-            return (int) findFirstNonNegativeForFlagsInStates(mProxyUids,
-                    uidState, uidState, flags);
-        }
-
-        /**
-         * Gets the package name of the app that performed the op on behalf of this
-         * app and as a result blamed the op on this app or {@code null}
-         * if there is no proxy.
-         *
-         * @return The proxy package name.
-         */
-        public @Nullable String getProxyPackageName() {
-            return findFirstNonNullForFlagsInStates(mProxyPackageNames, MAX_PRIORITY_UID_STATE,
-                    MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
-        }
-
-        /**
-         * Gets the package name of the app that performed the op on behalf of this
-         * app and as a result blamed the op on this app for a UID state or
-         * {@code null} if there is no proxy.
-         *
-         * @param uidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return The feature id.
-         */
-        public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) {
-            return findFirstNonNullForFlagsInStates(mProxyPackageNames, uidState, uidState, flags);
-        }
-
-        /**
-         * Gets the feature of the app that performed the op on behalf of this
-         * app and as a result blamed the op on this app or {@code null}
-         * if there is no proxy.
-         *
-         * @return The proxy package name.
-         */
-        public @Nullable String getProxyFeatureId() {
-            return findFirstNonNullForFlagsInStates(mProxyFeatureIds, MAX_PRIORITY_UID_STATE,
-                    MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
-        }
-
-        /**
-         * Gets the feature of the app that performed the op on behalf of this
-         * app and as a result blamed the op on this app for a UID state or
-         * {@code null} if there is no proxy.
-         *
-         * @param uidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return The feature id.
-         */
-        public @Nullable String getProxyFeatureId(@UidState int uidState, @OpFlags int flags) {
-            return findFirstNonNullForFlagsInStates(mProxyFeatureIds, uidState, uidState, flags);
-        }
-
-        /**
-         * @hide
-         */
-        public static class Builder {
-            private final boolean mRunning;
-
-            private final @Nullable LongSparseLongArray mAccessTimes;
-            private final @Nullable LongSparseLongArray mRejectTimes;
-            private final @Nullable LongSparseLongArray mDurations;
-            private final @Nullable LongSparseLongArray mProxyUids;
-            private final @Nullable LongSparseArray<String> mProxyPackageNames;
-            private final @Nullable LongSparseArray<String> mProxyFeatureIds;
-            private @NonNull OpEntry mParent;
-
-            public Builder(boolean running, @Nullable LongSparseLongArray accessTimes,
-                    @Nullable LongSparseLongArray rejectTimes,
-                    @Nullable LongSparseLongArray durations,
-                    @Nullable LongSparseLongArray proxyUids,
-                    @Nullable LongSparseArray<String> proxyPackageNames,
-                    @Nullable LongSparseArray<String> proxyFeatureIds) {
-                mRunning = running;
-                mAccessTimes = accessTimes;
-                mRejectTimes = rejectTimes;
-                mDurations = durations;
-                mProxyUids = proxyUids;
-                mProxyPackageNames = proxyPackageNames;
-                mProxyFeatureIds = proxyFeatureIds;
-            }
-
-            public Builder setParent(@NonNull OpEntry parent) {
-                mParent = parent;
-
-                return this;
-            }
-
-            /**
-             * Create OpFeatureEntry from builder
-             */
-            public OpFeatureEntry build() {
-                Preconditions.checkNotNull(mParent);
-
-                return new OpFeatureEntry(mParent, mRunning, mAccessTimes, mRejectTimes,
-                        mDurations, mProxyUids, mProxyPackageNames, mProxyFeatureIds);
-            }
-        }
-
-        /**
-         * @hide
-         */
-        public void writeToParcel(@NonNull Parcel dest, int flags) {
-            LongSparseLongArray.Parcelling longSparseLongArrayParcelling =
-                    LongSparseLongArray.Parcelling.Cache.getOrCreate(
-                            LongSparseLongArray.Parcelling.class);
-            LongSparseArray.StringParcelling longSparseStringArrayParcelling =
-                    LongSparseArray.StringParcelling.Cache.getOrCreate(
-                            LongSparseArray.StringParcelling.class);
-
-            dest.writeBoolean(mRunning);
-            longSparseLongArrayParcelling.parcel(mAccessTimes, dest, flags);
-            longSparseLongArrayParcelling.parcel(mRejectTimes, dest, flags);
-            longSparseLongArrayParcelling.parcel(mDurations, dest, flags);
-            longSparseLongArrayParcelling.parcel(mProxyUids, dest, flags);
-            longSparseStringArrayParcelling.parcel(mProxyPackageNames, dest, flags);
-            longSparseStringArrayParcelling.parcel(mProxyFeatureIds, dest, flags);
-        }
-
-        /**
-         * @hide
-         */
-        public static OpFeatureEntry.Builder createFromParcel(@NonNull Parcel source) {
-            LongSparseLongArray.Parcelling longSparseLongArrayParcelling =
-                    LongSparseLongArray.Parcelling.Cache.getOrCreate(
-                            LongSparseLongArray.Parcelling.class);
-            LongSparseArray.StringParcelling longSparseStringArrayParcelling =
-                    LongSparseArray.StringParcelling.Cache.getOrCreate(
-                            LongSparseArray.StringParcelling.class);
-
-            return new OpFeatureEntry.Builder(source.readBoolean(),
-                    longSparseLongArrayParcelling.unparcel(source),
-                    longSparseLongArrayParcelling.unparcel(source),
-                    longSparseLongArrayParcelling.unparcel(source),
-                    longSparseLongArrayParcelling.unparcel(source),
-                    longSparseStringArrayParcelling.unparcel(source),
-                    longSparseStringArrayParcelling.unparcel(source));
-        }
-    }
-
-    /**
-     * Class holding the information about one unique operation of an application.
-     * @hide
-     */
-    @TestApi
-    @Immutable
-    @SystemApi
+    // @DataClass(genHiddenConstructor = true) codegen verifier is broken
     public static final class OpEntry implements Parcelable {
+        /** The code of the op */
         private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
+        /** The mode of the op */
         private final @Mode int mMode;
-        private final @NonNull ArrayMap<String, OpFeatureEntry> mFeatures;
-
-        /**
-         * @hide
-         */
-        public OpEntry(@IntRange(from = 0, to = _NUM_OP - 1) int op, @Mode int mode,
-                @NonNull Pair<String, OpFeatureEntry.Builder>[] featureBuilders) {
-            mOp = Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "op");
-            mMode = Preconditions.checkArgumentInRange(mode, 0, MODE_FOREGROUND, "mode");
-
-            mFeatures = new ArrayMap<>(featureBuilders.length);
-            for (Pair<String, OpFeatureEntry.Builder> feature : featureBuilders) {
-                mFeatures.put(feature.first, feature.second.setParent(this).build());
-            }
-        }
-
-        /**
-         * @return The mapping from the feature ids to the feature state
-         */
-        public @NonNull Map<String, OpFeatureEntry> getFeatures() {
-            return mFeatures;
-        }
+        /** The features that have been used when checking the op */
+        private final @NonNull Map<String, OpFeatureEntry> mFeatures;
 
         /**
          * @hide
@@ -2882,16 +3225,9 @@
         }
 
         /**
-         * @return this entry's current mode, such as {@link #MODE_ALLOWED}.
-         */
-        public @Mode int getMode() {
-            return mMode;
-        }
-
-        /**
-         * @deprecated Use {@link OpEntry#getLastAccessTime(int)} instead
-         *
          * @hide
+         *
+         * @deprecated Use {@link #getLastAccessTime(int)} instead
          */
         @Deprecated
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
@@ -2900,130 +3236,112 @@
             return getLastAccessTime(OP_FLAGS_ALL);
         }
 
-        private long getMaxOfFeatures(@NonNull ToLongFunction<OpFeatureEntry> timeGetter) {
-            long max = 0;
-
-            int numFeatures = mFeatures.size();
-            for (int i = 0; i < numFeatures; i++) {
-                max = Math.max(max, timeGetter.applyAsLong(mFeatures.valueAt(i)));
-            }
-
-            return max;
-        }
-
-        private long getSumOfFeatures(@NonNull ToLongFunction<OpFeatureEntry> getter) {
-            long sum = 0;
-
-            int numFeatures = mFeatures.size();
-            for (int i = 0; i < numFeatures; i++) {
-                sum += getter.applyAsLong(mFeatures.valueAt(i));
-            }
-
-            return sum;
-        }
-
         /**
-         * Return the last wall clock time  in milliseconds this op was accessed
-         * by the app for a given range of UID states.
+         * Return the last access time.
          *
-         * @param fromUidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state for which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
+         * @param flags The op flags
          *
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
          *
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessBackgroundTime(int)
-         * @see #getLastAccessTime(int)
+         * @see #getLastAccessTime(int, int, int)
+         * @see OpFeatureEntry#getLastAccessTime(int)
          */
         public long getLastAccessTime(@OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastAccessTime(flags)));
+            return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Return the last wall clock time in milliseconds this op was accessed
-         * by the app while in the foreground.
+         * Return the last foreground access time.
          *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @param flags The op flags
          *
-         * @see #getLastAccessBackgroundTime(int)
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
          * @see #getLastAccessTime(int)
+         * @see #getLastAccessBackgroundTime(int)
          * @see #getLastAccessTime(int, int, int)
+         * @see OpFeatureEntry#getLastAccessForegroundTime(int)
          */
         public long getLastAccessForegroundTime(@OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastAccessForegroundTime(flags)));
+            return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
         }
 
         /**
-         * Return the last wall clock time in milliseconds this op was accessed
-         * by the app while in the background.
+         * Return the last background access time.
          *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @param flags The op flags
          *
-         * @see #getLastAccessForegroundTime(int)
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
          * @see #getLastAccessTime(int)
+         * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessTime(int, int, int)
+         * @see OpFeatureEntry#getLastAccessBackgroundTime(int)
          */
         public long getLastAccessBackgroundTime(@OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastAccessBackgroundTime(flags)));
+            return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
         }
 
         /**
-         * Return the last wall clock time  in milliseconds this op was accessed
-         * by the app for a given range of UID states.
+         * Return the last access event.
          *
-         * @param fromUidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state for which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
+         * @param flags The op flags
          *
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @return the last access event of {@code null}
+         */
+        private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
+                @UidState int toUidState, @OpFlags int flags) {
+            NoteOpEvent lastAccessEvent = null;
+            for (OpFeatureEntry featureEntry : mFeatures.values()) {
+                NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastAccessEvent(fromUidState,
+                        toUidState, flags);
+
+                if (lastAccessEvent == null || (lastFeatureAccessEvent != null
+                        && lastFeatureAccessEvent.noteTime > lastAccessEvent.noteTime)) {
+                    lastAccessEvent = lastFeatureAccessEvent;
+                }
+            }
+
+            return lastAccessEvent;
+        }
+
+        /**
+         * Return the last access time.
          *
+         * @param fromUidState the lowest uid state to query
+         * @param toUidState the highest uid state to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return the last access time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessBackgroundTime(int)
-         * @see #getLastAccessTime(int)
+         * @see OpFeatureEntry#getLastAccessTime(int, int, int)
          */
         public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
                 @OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastAccessTime(fromUidState,
-                            toUidState, flags)));
+            NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);;
+
+            if (lastEvent == null) {
+                return -1;
+            }
+
+            return lastEvent.noteTime;
         }
 
         /**
-         * @deprecated Use {@link OpEntry#getLastRejectTime(int)} instead
-         *
          * @hide
+         *
+         * @deprecated Use {@link #getLastRejectTime(int)} instead
          */
         @Deprecated
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
@@ -3033,107 +3351,113 @@
         }
 
         /**
-         * Return the last wall clock time in milliseconds the app made an attempt
-         * to access this op but was rejected.
+         * Return the last rejection time.
          *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last reject time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @param flags The op flags
          *
-         * @see #getLastRejectBackgroundTime(int)
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
          * @see #getLastRejectForegroundTime(int)
+         * @see #getLastRejectBackgroundTime(int)
          * @see #getLastRejectTime(int, int, int)
+         * @see OpFeatureEntry#getLastRejectTime(int)
          */
         public long getLastRejectTime(@OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastRejectTime(flags)));
+            return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Return the last wall clock time in milliseconds the app made an attempt
-         * to access this op while in the foreground but was rejected.
+         * Return the last foreground rejection time.
          *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground reject time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @param flags The op flags
          *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
          * @see #getLastRejectBackgroundTime(int)
          * @see #getLastRejectTime(int, int, int)
-         * @see #getLastRejectTime(int)
+         * @see OpFeatureEntry#getLastRejectForegroundTime(int)
          */
         public long getLastRejectForegroundTime(@OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastRejectForegroundTime(flags)));
+            return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
         }
 
         /**
-         * Return the last wall clock time in milliseconds the app made an attempt
-         * to access this op while in the background but was rejected.
+         * Return the last background rejection time.
          *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last background reject time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @param flags The op flags
          *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectTime(int, int, int)
-         * @see #getLastRejectTime(int)
+         * @see OpFeatureEntry#getLastRejectBackgroundTime(int)
          */
         public long getLastRejectBackgroundTime(@OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastRejectBackgroundTime(flags)));
+            return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
         }
 
         /**
-         * Return the last wall clock time state in milliseconds the app made an
-         * attempt to access this op for a given range of UID states.
+         * Return the last rejection event.
          *
-         * @param fromUidState The UID state from which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state to which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the last foreground access time in milliseconds since
-         * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+         * @param flags The op flags
          *
+         * @return the last reject event of {@code null}
+         */
+        private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
+                @UidState int toUidState, @OpFlags int flags) {
+            NoteOpEvent lastAccessEvent = null;
+            for (OpFeatureEntry featureEntry : mFeatures.values()) {
+                NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastRejectEvent(fromUidState,
+                        toUidState, flags);
+
+                if (lastAccessEvent == null || (lastFeatureAccessEvent != null
+                        && lastFeatureAccessEvent.noteTime > lastAccessEvent.noteTime)) {
+                    lastAccessEvent = lastFeatureAccessEvent;
+                }
+            }
+
+            return lastAccessEvent;
+        }
+
+        /**
+         * Return the last rejection time.
+         *
+         * @param fromUidState the lowest uid state to query
+         * @param toUidState the highest uid state to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         *
+         * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectBackgroundTime(int)
-         * @see #getLastRejectTime(int)
+         * @see #getLastRejectTime(int, int, int)
+         * @see OpFeatureEntry#getLastRejectTime(int, int, int)
          */
         public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
                 @OpFlags int flags) {
-            return getMaxOfFeatures(
-                    (featureEntry -> featureEntry.getLastRejectTime(fromUidState,
-                            toUidState, flags)));
+            NoteOpEvent lastEvent = getLastRejectEvent(fromUidState, toUidState, flags);
+            if (lastEvent == null) {
+                return -1;
+            }
+
+            return lastEvent.noteTime;
         }
 
         /**
          * @return Whether the operation is running.
          */
         public boolean isRunning() {
-            int numFeatures = mFeatures.size();
-            if (mFeatures.isEmpty()) {
-                return false;
-            }
-
-            for (int i = 0; i < numFeatures; i++) {
-                if (mFeatures.valueAt(i).mRunning) {
+            for (OpFeatureEntry opFeatureEntry : mFeatures.values()) {
+                if (opFeatureEntry.isRunning()) {
                     return true;
                 }
             }
@@ -3142,257 +3466,349 @@
         }
 
         /**
-         * @return The duration of the operation in milliseconds. The duration is in wall time.
+         * @deprecated Use {@link #getLastDuration(int)} instead
          */
+        @Deprecated
         public long getDuration() {
-            return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+            return getLastDuration(OP_FLAGS_ALL);
         }
 
         /**
-         * Return the duration in milliseconds the app accessed this op while
-         * in the foreground. The duration is in wall time.
+         * Return the duration in milliseconds of the last the access.
          *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the foreground access duration in milliseconds.
+         * @param flags The op flags
          *
-         * @see #getLastBackgroundDuration(int)
-         * @see #getLastDuration(int, int, int)
-         */
-        public long getLastForegroundDuration(@OpFlags int flags) {
-            return getSumOfFeatures((featureEntry) ->
-                    featureEntry.getLastForegroundDuration(flags));
-        }
-
-        /**
-         * Return the duration in milliseconds the app accessed this op while
-         * in the background. The duration is in wall time.
-         *
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the background access duration in milliseconds.
+         * @return the duration in milliseconds or {@code -1}
          *
          * @see #getLastForegroundDuration(int)
+         * @see #getLastBackgroundDuration(int)
          * @see #getLastDuration(int, int, int)
+         * @see OpFeatureEntry#getLastDuration(int)
          */
-        public long getLastBackgroundDuration(@OpFlags int flags) {
-            return getSumOfFeatures((featureEntry) ->
-                    featureEntry.getLastBackgroundDuration(flags));
+        public long getLastDuration(@OpFlags int flags) {
+            return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Return the duration in milliseconds the app accessed this op for
-         * a given range of UID states. The duration is in wall time.
+         * Return the duration in milliseconds of the last foreground access.
          *
-         * @param fromUidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param toUidState The UID state for which to query.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return the access duration in milliseconds.
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastDuration(int)
+         * @see #getLastBackgroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         * @see OpFeatureEntry#getLastForegroundDuration(int)
+         */
+        public long getLastForegroundDuration(@OpFlags int flags) {
+            return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
+        }
+
+        /**
+         * Return the duration in milliseconds of the last background access.
+         *
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastDuration(int)
+         * @see #getLastForegroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         * @see OpFeatureEntry#getLastBackgroundDuration(int)
+         */
+        public long getLastBackgroundDuration(@OpFlags int flags) {
+            return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
+        }
+
+        /**
+         * Return the duration in milliseconds of the last access.
+         *
+         * @param fromUidState The lowest UID state for which to query
+         * @param toUidState The highest UID state for which to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return the duration in milliseconds or {@code -1}
+         *
+         * @see #getLastDuration(int)
+         * @see #getLastForegroundDuration(int)
+         * @see #getLastBackgroundDuration(int)
+         * @see OpFeatureEntry#getLastDuration(int, int, int)
          */
         public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
                 @OpFlags int flags) {
-            return getSumOfFeatures((featureEntry) ->
-                    featureEntry.getLastDuration(fromUidState, toUidState, flags));
-        }
-
-        /**
-         * Like {@link #findFirstNonNegativeForFlagsInStates(LongSparseLongArray, int, int, int)}
-         * but for all proxy uid in all features.
-         */
-        private long findFirstNonNegativeProxyUidInFeatureStates(@UidState int beginUidState,
-                @UidState int endUidState, @OpFlags int flags) {
-            int numFeatures = mFeatures.size();
-
-            if (numFeatures == 0) {
+            NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
+            if (lastEvent == null) {
                 return -1;
             }
 
-            while (flags != 0) {
-                final int flag = 1 << Integer.numberOfTrailingZeros(flags);
-                flags &= ~flag;
-                for (int uidState : UID_STATES) {
-                    if (uidState < beginUidState || uidState > endUidState) {
-                        continue;
-                    }
-
-                    final long key = makeKey(uidState, flag);
-
-                    for (int i = 0; i < numFeatures; i++) {
-                        OpFeatureEntry featureEntry = mFeatures.valueAt(i);
-
-                        if (featureEntry.mProxyUids == null) {
-                            continue;
-                        }
-
-                        final long proxyUid = featureEntry.mProxyUids.get(key);
-                        if (proxyUid >= 0) {
-                            return proxyUid;
-                        }
-                    }
-                }
-            }
-
-            return -1;
+            return lastEvent.duration;
         }
 
         /**
-         * Like {@link #findFirstNonNullForFlagsInStates(LongSparseArray, int, int, int)} but
-         * for all proxyPackageNames in all features.
-         */
-        private @Nullable String findFirstNonNullProxyPackageNameInFeatureStates(
-                @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) {
-            int numFeatures = mFeatures.size();
-
-            if (numFeatures == 0) {
-                return null;
-            }
-
-            while (flags != 0) {
-                final int flag = 1 << Integer.numberOfTrailingZeros(flags);
-                flags &= ~flag;
-                for (int uidState : UID_STATES) {
-                    if (uidState < beginUidState || uidState > endUidState) {
-                        continue;
-                    }
-                    final long key = makeKey(uidState, flag);
-
-                    for (int i = 0; i < numFeatures; i++) {
-                        OpFeatureEntry featureEntry = mFeatures.valueAt(i);
-
-                        if (featureEntry.mProxyPackageNames == null) {
-                            continue;
-                        }
-
-                        final String proxyName = featureEntry.mProxyPackageNames.get(key);
-                        if (proxyName != null) {
-                            return proxyName;
-                        }
-                    }
-                }
-            }
-            return null;
-        }
-
-        /**
-         * @deprecated Use {@link #getProxyUid(int, int)} instead
+         * @deprecated Use {@link #getLastProxyInfo(int)} instead
          */
         @Deprecated
         public int getProxyUid() {
-            return (int) findFirstNonNegativeProxyUidInFeatureStates(MAX_PRIORITY_UID_STATE,
-                    MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+            OpEventProxyInfo proxy = getLastProxyInfo(OP_FLAGS_ALL);
+            if (proxy == null) {
+                return Process.INVALID_UID;
+            }
+
+            return proxy.getUid();
         }
 
         /**
-         * Gets the UID of the app that performed the op on behalf of this app and
-         * as a result blamed the op on this app or {@link Process#INVALID_UID} if
-         * there is no proxy.
-         *
-         * @param uidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         *
-         * @return The proxy UID.
+         * @deprecated Use {@link #getLastProxyInfo(int)} instead
          */
+        @Deprecated
         public int getProxyUid(@UidState int uidState, @OpFlags int flags) {
-            return (int) findFirstNonNegativeProxyUidInFeatureStates(uidState, uidState, flags);
+            OpEventProxyInfo proxy = getLastProxyInfo(uidState, uidState, flags);
+            if (proxy == null) {
+                return Process.INVALID_UID;
+            }
+
+            return proxy.getUid();
         }
 
         /**
-         * @deprecated Use {@link #getProxyPackageName(int, int)} instead
+         * @deprecated Use {@link #getLastProxyInfo(int)} instead
          */
         @Deprecated
         public @Nullable String getProxyPackageName() {
-            return findFirstNonNullProxyPackageNameInFeatureStates(MAX_PRIORITY_UID_STATE,
-                    MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+            OpEventProxyInfo proxy = getLastProxyInfo(OP_FLAGS_ALL);
+            if (proxy == null) {
+                return null;
+            }
+
+            return proxy.getPackageName();
         }
 
         /**
-         * Gets the package name of the app that performed the op on behalf of this
-         * app and as a result blamed the op on this app for a UID state or
-         * {@code null} if there is no proxy.
-         *
-         * @param uidState The UID state for which to query. Could be one of
-         * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
-         * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
-         * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
-         * @param flags The flags which are any combination of
-         * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
-         * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
-         * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
-         * for any flag.
-         * @return The proxy package name.
+         * @deprecated Use {@link #getLastProxyInfo(int)} instead
          */
+        @Deprecated
         public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) {
-            return findFirstNonNullProxyPackageNameInFeatureStates(uidState, uidState, flags);
+            OpEventProxyInfo proxy = getLastProxyInfo(uidState, uidState, flags);
+            if (proxy == null) {
+                return null;
+            }
+
+            return proxy.getPackageName();
         }
 
         /**
-         * Create OpEntry from parcel.
+         * Gets the proxy info of the app that performed the last access on behalf of this app and
+         * as a result blamed the op on this app.
          *
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastForegroundProxyInfo(int)
+         * @see #getLastBackgroundProxyInfo(int)
+         * @see #getLastProxyInfo(int, int, int)
+         * @see OpFeatureEntry#getLastProxyInfo(int)
+         */
+        public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) {
+            return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last foreground access on behalf of
+         * this app and as a result blamed the op on this app.
+         *
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastProxyInfo(int)
+         * @see #getLastBackgroundProxyInfo(int)
+         * @see #getLastProxyInfo(int, int, int)
+         * @see OpFeatureEntry#getLastForegroundProxyInfo(int)
+         */
+        public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) {
+            return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
+                    flags);
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last background access on behalf of
+         * this app and as a result blamed the op on this app.
+         *
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastProxyInfo(int)
+         * @see #getLastForegroundProxyInfo(int)
+         * @see #getLastProxyInfo(int, int, int)
+         * @see OpFeatureEntry#getLastBackgroundProxyInfo(int)
+         */
+        public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) {
+            return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
+                    flags);
+        }
+
+        /**
+         * Gets the proxy info of the app that performed the last access on behalf of this app and
+         * as a result blamed the op on this app.
+         *
+         * @param fromUidState The lowest UID state for which to query
+         * @param toUidState The highest UID state for which to query (inclusive)
+         * @param flags The op flags
+         *
+         * @return The proxy name or {@code null}
+         *
+         * @see #getLastProxyInfo(int)
+         * @see #getLastForegroundProxyInfo(int)
+         * @see #getLastBackgroundProxyInfo(int)
+         * @see OpFeatureEntry#getLastProxyInfo(int, int, int)
+         */
+        public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState,
+                @UidState int toUidState, @OpFlags int flags) {
+            NoteOpEvent lastEvent = getLastAccessEvent(fromUidState, toUidState, flags);
+            if (lastEvent == null) {
+                return null;
+            }
+
+            return lastEvent.proxy;
+        }
+
+
+
+        // Code below generated by codegen v1.0.14.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
+
+
+        /**
+         * Creates a new OpEntry.
+         *
+         * @param op
+         *   The code of the op
+         * @param mode
+         *   The mode of the op
+         * @param features
+         *   The features that have been used when checking the op
          * @hide
          */
-        public static OpEntry createFromParcel(@NonNull Parcel source) {
-            int op = source.readInt();
-            int mode = source.readInt();
+        @DataClass.Generated.Member
+        public OpEntry(
+                @IntRange(from = 0, to = _NUM_OP - 1) int op,
+                @Mode int mode,
+                @NonNull Map<String,OpFeatureEntry> features) {
+            this.mOp = op;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, mOp,
+                    "from", 0,
+                    "to", _NUM_OP - 1);
+            this.mMode = mode;
+            com.android.internal.util.AnnotationValidations.validate(
+                    Mode.class, null, mMode);
+            this.mFeatures = features;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, mFeatures);
 
-            int numFeatures = source.readInt();
-            Pair<String, OpFeatureEntry.Builder>[] features = new Pair[numFeatures];
-            for (int i = 0; i < numFeatures; i++) {
-                features[i] = new Pair<>(source.readString(),
-                        OpFeatureEntry.createFromParcel(source));
-            }
+            // onConstructed(); // You can define this method to get a callback
+        }
 
-            return new OpEntry(op, mode, features);
+        /**
+         * The mode of the op
+         */
+        @DataClass.Generated.Member
+        public @Mode int getMode() {
+            return mMode;
+        }
+
+        /**
+         * The features that have been used when checking the op
+         */
+        @DataClass.Generated.Member
+        public @NonNull Map<String,OpFeatureEntry> getFeatures() {
+            return mFeatures;
         }
 
         @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
+        @DataClass.Generated.Member
         public void writeToParcel(Parcel dest, int flags) {
+            // You can override field parcelling by defining methods like:
+            // void parcelFieldName(Parcel dest, int flags) { ... }
+
             dest.writeInt(mOp);
             dest.writeInt(mMode);
-
-            int numFeatures = mFeatures.size();
-            dest.writeInt(numFeatures);
-            for (int i = 0; i < numFeatures; i++) {
-                dest.writeString(mFeatures.keyAt(i));
-                mFeatures.valueAt(i).writeToParcel(dest, flags);
-            }
+            dest.writeMap(mFeatures);
         }
 
-        public static final @NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
+        @Override
+        @DataClass.Generated.Member
+        public int describeContents() { return 0; }
+
+        /** @hide */
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        @DataClass.Generated.Member
+        /* package-private */ OpEntry(@NonNull Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            int op = in.readInt();
+            int mode = in.readInt();
+            Map<String,OpFeatureEntry> features = new java.util.LinkedHashMap<>();
+            in.readMap(features, OpFeatureEntry.class.getClassLoader());
+
+            this.mOp = op;
+            com.android.internal.util.AnnotationValidations.validate(
+                    IntRange.class, null, mOp,
+                    "from", 0,
+                    "to", _NUM_OP - 1);
+            this.mMode = mode;
+            com.android.internal.util.AnnotationValidations.validate(
+                    Mode.class, null, mMode);
+            this.mFeatures = features;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, mFeatures);
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @DataClass.Generated.Member
+        public static final @NonNull Parcelable.Creator<OpEntry> CREATOR
+                = new Parcelable.Creator<OpEntry>() {
             @Override
-            public @NonNull OpEntry createFromParcel(@NonNull Parcel parcel) {
-                return OpEntry.createFromParcel(parcel);
+            public OpEntry[] newArray(int size) {
+                return new OpEntry[size];
             }
 
             @Override
-            public @NonNull OpEntry[] newArray(int size) {
-                return new OpEntry[size];
+            public OpEntry createFromParcel(@NonNull Parcel in) {
+                return new OpEntry(in);
             }
         };
+
+        /*
+        @DataClass.Generated(
+                time = 1574809856259L,
+                codegenVersion = "1.0.14",
+                sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
+                inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpFeatureEntry> mFeatures\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic  long getAccessTime(int,int)\npublic  long getRejectTime(int,int)\npublic  boolean isRunning()\nprivate  android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic  long getDuration(int,int)\npublic  int getProxyUid(int,int)\nprivate  int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+        @Deprecated
+        private void __metadata() {}
+         */
+
+
+        //@formatter:on
+        // End of generated code
+
     }
 
     /** @hide */
@@ -4904,71 +5320,6 @@
     }
 
     /**
-     * Finds the first non-negative value for the given flags in between the begin and
-     * end UID states.
-     *
-     * @param counts The data array.
-     * @param beginUidState The beginning UID state (inclusive).
-     * @param endUidState The end UID state (inclusive).
-     * @param flags The UID flags.
-     * @return The non-negative value or -1.
-     */
-    private static long findFirstNonNegativeForFlagsInStates(@Nullable LongSparseLongArray counts,
-            @UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
-        if (counts == null) {
-            return -1;
-        }
-        while (flags != 0) {
-            final int flag = 1 << Integer.numberOfTrailingZeros(flags);
-            flags &= ~flag;
-            for (int uidState : UID_STATES) {
-                if (uidState < beginUidState || uidState > endUidState) {
-                    continue;
-                }
-                final long key = makeKey(uidState, flag);
-                final long value = counts.get(key);
-                if (value >= 0) {
-                    return value;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Finds the first non-null value for the given flags in between the begin and
-     * end UID states.
-     *
-     * @param counts The data array.
-     * @param beginUidState The beginning UID state (inclusive).
-     * @param endUidState The end UID state (inclusive).
-     * @param flags The UID flags.
-     * @return The non-negative value or -1.
-     */
-    private static @Nullable String findFirstNonNullForFlagsInStates(
-            @Nullable LongSparseArray<String> counts, @UidState int beginUidState,
-            @UidState int endUidState, @OpFlags int flags) {
-        if (counts == null) {
-            return null;
-        }
-        while (flags != 0) {
-            final int flag = 1 << Integer.numberOfTrailingZeros(flags);
-            flags &= ~flag;
-            for (int uidState : UID_STATES) {
-                if (uidState < beginUidState || uidState > endUidState) {
-                    continue;
-                }
-                final long key = makeKey(uidState, flag);
-                final String value = counts.get(key);
-                if (value != null) {
-                    return value;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
      * Callback for notification of changes to operation state.
      */
     public interface OnOpChangedListener {
@@ -5126,6 +5477,7 @@
      *
      * @hide
      */
+    @TestApi
     @SystemApi
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
     public @NonNull List<AppOpsManager.PackageOps> getOpsForPackage(int uid,
@@ -6149,22 +6501,28 @@
         }
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * @deprecated Use own local {@link android.os.Binder#Binder()}
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Create own "
+            + "local {@link android.os.Binder}")
     public static IBinder getToken(IAppOpsService service) {
-        synchronized (AppOpsManager.class) {
-            if (sToken != null) {
-                return sToken;
-            }
-            try {
-                sToken = service.getToken(new Binder());
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-            return sToken;
-        }
+        return getClientId();
     }
 
+    /** @hide */
+    public static IBinder getClientId() {
+        synchronized (AppOpsManager.class) {
+            if (sClientId == null) {
+                sClientId = new Binder();
+            }
+
+            return sClientId;
+        }
+    }
 
     /**
      * @deprecated use {@link #startOp(String, int, String, String, String)} instead
@@ -6221,7 +6579,7 @@
      * the package is not in the passed in UID.
      */
     public int startOp(@NonNull String op, int uid, @Nullable String packageName,
-            @NonNull String featureId, @Nullable String message) {
+            @Nullable String featureId, @Nullable String message) {
         return startOp(strOpToOp(op), uid, packageName, false, featureId, message);
     }
 
@@ -6245,7 +6603,7 @@
      * @hide
      */
     public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
-            @NonNull String featureId, @Nullable String message) {
+            @Nullable String featureId, @Nullable String message) {
         final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, featureId,
                 message);
         if (mode == MODE_ERRORED) {
@@ -6321,7 +6679,7 @@
     public int startOpNoThrow(int op, int uid, @NonNull String packageName,
             boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) {
         try {
-            int mode = mService.startOperation(getToken(mService), op, uid, packageName,
+            int mode = mService.startOperation(getClientId(), op, uid, packageName,
                     featureId, startIfModeDefault);
             if (mode == MODE_ALLOWED) {
                 markAppOpNoted(uid, packageName, op, featureId, message);
@@ -6381,7 +6739,7 @@
     public void finishOp(int op, int uid, @NonNull String packageName,
             @Nullable String featureId) {
         try {
-            mService.finishOperation(getToken(mService), op, uid, packageName, featureId);
+            mService.finishOperation(getClientId(), op, uid, packageName, featureId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -6946,22 +7304,23 @@
     }
 
     /**
-     * Computes the max for the given flags in between the begin and
-     * end UID states.
+     * Gets the last of the event.
      *
-     * @param counts The data array.
-     * @param flags The UID flags.
-     * @param beginUidState The beginning UID state (exclusive).
-     * @param endUidState The end UID state.
-     * @return The sum.
+     * @param events The events
+     * @param flags The UID flags
+     * @param beginUidState The maximum UID state (inclusive)
+     * @param endUidState The minimum UID state (inclusive)
+     *
+     * @return The last event of {@code null}
      */
-    private static long maxForFlagsInStates(@Nullable LongSparseLongArray counts,
-            @UidState int beginUidState, @UidState int endUidState,
-            @OpFlags int flags) {
-        if (counts == null) {
-            return 0;
+    private static @Nullable NoteOpEvent getLastEvent(
+            @Nullable LongSparseArray<NoteOpEvent> events, @UidState int beginUidState,
+            @UidState int endUidState, @OpFlags int flags) {
+        if (events == null) {
+            return null;
         }
-        long max = 0;
+
+        NoteOpEvent lastEvent = null;
         while (flags != 0) {
             final int flag = 1 << Integer.numberOfTrailingZeros(flags);
             flags &= ~flag;
@@ -6970,12 +7329,16 @@
                     continue;
                 }
                 final long key = makeKey(uidState, flag);
-                max = Math.max(max, counts.get(key));
+
+                NoteOpEvent event = events.get(key);
+                if (lastEvent == null || event != null && event.noteTime > lastEvent.noteTime) {
+                    lastEvent = event;
+                }
             }
         }
-        return max;
-    }
 
+        return lastEvent;
+    }
 
     private static void writeLongSparseLongArrayToParcel(
             @Nullable LongSparseLongArray array, @NonNull Parcel parcel) {
@@ -7004,33 +7367,6 @@
         return array;
     }
 
-    private static void writeLongSparseStringArrayToParcel(
-            @Nullable LongSparseArray<String> array, @NonNull Parcel parcel) {
-        if (array != null) {
-            final int size = array.size();
-            parcel.writeInt(size);
-            for (int i = 0; i < size; i++) {
-                parcel.writeLong(array.keyAt(i));
-                parcel.writeString(array.valueAt(i));
-            }
-        } else {
-            parcel.writeInt(-1);
-        }
-    }
-
-    private static @Nullable LongSparseArray<String> readLongSparseStringArrayFromParcel(
-            @NonNull Parcel parcel) {
-        final int size = parcel.readInt();
-        if (size < 0) {
-            return null;
-        }
-        final LongSparseArray<String> array = new LongSparseArray<>(size);
-        for (int i = 0; i < size; i++) {
-            array.append(parcel.readLong(), parcel.readString());
-        }
-        return array;
-    }
-
     /**
      * Collects the keys from an array to the result creating the result if needed.
      *
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ce21db3..83e4b00 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -120,8 +120,8 @@
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
-import android.net.wifi.WifiCondManager;
 import android.net.wifi.WifiFrameworkInitializer;
+import android.net.wifi.wificond.WifiCondManager;
 import android.nfc.NfcManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -1147,14 +1147,6 @@
                         return new TimeZoneDetector();
                     }});
 
-        registerService(Context.TELEPHONY_IMS_SERVICE, android.telephony.ims.ImsManager.class,
-                new CachedServiceFetcher<android.telephony.ims.ImsManager>() {
-                    @Override
-                    public android.telephony.ims.ImsManager createService(ContextImpl ctx) {
-                        return new android.telephony.ims.ImsManager(ctx.getOuterContext());
-                    }
-                });
-
         registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
                 new CachedServiceFetcher<PermissionManager>() {
                     @Override
diff --git a/core/java/android/content/pm/DataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java
index b163861..af4b99a 100644
--- a/core/java/android/content/pm/DataLoaderParams.java
+++ b/core/java/android/content/pm/DataLoaderParams.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.ParcelFileDescriptor;
 
 import java.util.Arrays;
@@ -26,9 +27,12 @@
 
 /**
  * This class represents the parameters used to configure an Incremental Data Loader.
- * Hide for now.
+ *
+ * WARNING: This is a system API to aid internal development.
+ * Use at your own risk. It will change or be removed without warning.
  * @hide
  */
+@SystemApi
 public class DataLoaderParams {
     @NonNull private final DataLoaderParamsParcel mData;
 
@@ -52,6 +56,9 @@
         mData = data;
     }
 
+    /**
+     * @hide
+     */
     public DataLoaderParams(@NonNull DataLoaderParamsParcel data) {
         mData = data;
     }
@@ -70,6 +77,9 @@
         return mData.packageName;
     }
 
+    /**
+     * @hide
+     */
     public final @NonNull DataLoaderParamsParcel getData() {
         return mData;
     }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 5386422..3d6d849 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1082,8 +1082,12 @@
          * @throws SecurityException if called after the session has been
          *             sealed or abandoned
          * @throws IllegalStateException if called for non-callback session
+         *
+         * WARNING: This is a system API to aid internal development.
+         * Use at your own risk. It will change or be removed without warning.
          * {@hide}
          */
+        @SystemApi
         public void addFile(@NonNull String name, long lengthBytes, @NonNull byte[] metadata) {
             try {
                 mSession.addFile(name, lengthBytes, metadata);
@@ -1461,6 +1465,8 @@
         /** TODO(b/146080380): add a class name to make it fully compatible with ComponentName.
          * {@hide} */
         public String dataLoaderPackageName;
+        /** {@hide} */
+        public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
 
         /**
          * Construct parameters for a new package install session.
@@ -1501,6 +1507,7 @@
                         dataLoaderParamsParcel);
             }
             dataLoaderPackageName = source.readString();
+            rollbackDataPolicy = source.readInt();
         }
 
         /** {@hide} */
@@ -1526,6 +1533,7 @@
             ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
             ret.incrementalParams = incrementalParams;
             ret.dataLoaderPackageName = dataLoaderPackageName;
+            ret.rollbackDataPolicy = rollbackDataPolicy;
             return ret;
         }
 
@@ -1682,12 +1690,14 @@
         }
 
         /**
-         * Request that rollbacks be enabled or disabled for the given upgrade.
+         * Request that rollbacks be enabled or disabled for the given upgrade with rollback data
+         * policy set to RESTORE.
          *
          * <p>If the parent session is staged or has rollback enabled, all children sessions
          * must have the same properties.
          *
          * @param enable set to {@code true} to enable, {@code false} to disable
+         * @see SessionParams#setEnableRollback(boolean, int)
          * @hide
          */
         @SystemApi @TestApi
@@ -1697,9 +1707,36 @@
             } else {
                 installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
             }
+            rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
         }
 
         /**
+         * Request that rollbacks be enabled or disabled for the given upgrade.
+         *
+         * <p>If the parent session is staged or has rollback enabled, all children sessions
+         * must have the same properties.
+         *
+         * <p> For a multi-package install, this method must be called on each child session to
+         * specify rollback data policies explicitly. Note each child session is allowed to have
+         * different policies.
+         *
+         * @param enable set to {@code true} to enable, {@code false} to disable
+         * @param dataPolicy the rollback data policy for this session
+         * @hide
+         */
+        @SystemApi @TestApi
+        public void setEnableRollback(boolean enable,
+                @PackageManager.RollbackDataPolicy int dataPolicy) {
+            if (enable) {
+                installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+            }
+            rollbackDataPolicy = dataPolicy;
+        }
+
+
+        /**
          * @deprecated use {@link #setRequestDowngrade(boolean)}.
          * {@hide}
          */
@@ -1857,9 +1894,11 @@
 
         /**
          * Set Incremental data loader params.
-         *
+         * WARNING: This is a system API to aid internal development.
+         * Use at your own risk. It will change or be removed without warning.
          * {@hide}
          */
+        @SystemApi
         @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
         public void setIncrementalParams(@NonNull DataLoaderParams incrementalParams) {
             this.incrementalParams = incrementalParams;
@@ -1900,6 +1939,7 @@
             pw.printPair("isStaged", isStaged);
             pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
             pw.printPair("dataLoaderPackageName", dataLoaderPackageName);
+            pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
             pw.println();
         }
 
@@ -1935,6 +1975,7 @@
                 dest.writeParcelable(null, flags);
             }
             dest.writeString(dataLoaderPackageName);
+            dest.writeInt(rollbackDataPolicy);
         }
 
         public static final Parcelable.Creator<SessionParams>
@@ -2072,6 +2113,9 @@
         public long updatedMillis;
 
         /** {@hide} */
+        public int rollbackDataPolicy;
+
+        /** {@hide} */
         @UnsupportedAppUsage
         public SessionInfo() {
         }
@@ -2114,6 +2158,7 @@
             mStagedSessionErrorCode = source.readInt();
             mStagedSessionErrorMessage = source.readString();
             isCommitted = source.readBoolean();
+            rollbackDataPolicy = source.readInt();
         }
 
         /**
@@ -2431,6 +2476,17 @@
         }
 
         /**
+         * Return the data policy associated with the rollback for the given upgrade.
+         *
+         * @hide
+         */
+        @SystemApi @TestApi
+        @PackageManager.RollbackDataPolicy
+        public int getRollbackDataPolicy() {
+            return rollbackDataPolicy;
+        }
+
+        /**
          * Returns {@code true} if this session is an active staged session.
          *
          * We consider a session active if it has been committed and it is either pending
@@ -2592,6 +2648,7 @@
             dest.writeInt(mStagedSessionErrorCode);
             dest.writeString(mStagedSessionErrorMessage);
             dest.writeBoolean(isCommitted);
+            dest.writeInt(rollbackDataPolicy);
         }
 
         public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ee27f4f..f792127 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -710,6 +710,29 @@
     public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4;
 
     /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            RollbackDataPolicy.RESTORE,
+            RollbackDataPolicy.WIPE,
+            RollbackDataPolicy.RETAIN
+    })
+    public @interface RollbackDataPolicy {
+        /**
+         * User data will be backed up during install and restored during rollback.
+         */
+        int RESTORE = 0;
+        /**
+         * User data won't be backed up during install but will be wiped out during rollback.
+         */
+        int WIPE = 1;
+        /**
+         * User data won't be backed up during install and won't be restored during rollback.
+         * TODO: Not implemented yet.
+         */
+        int RETAIN = 2;
+    }
+
+    /** @hide */
     @IntDef(flag = true, prefix = { "INSTALL_" }, value = {
             INSTALL_REPLACE_EXISTING,
             INSTALL_ALLOW_TEST,
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 89a1c6a..a0f089b 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -248,6 +248,17 @@
     @TestApi
     public static final int PROTECTION_FLAG_TELEPHONY = 0x400000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>companion</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_COMPANION = 0x800000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -270,6 +281,7 @@
             PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
             PROTECTION_FLAG_APP_PREDICTOR,
             PROTECTION_FLAG_TELEPHONY,
+            PROTECTION_FLAG_COMPANION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index dd2c0d4..848b6d5 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -39,4 +39,5 @@
     boolean setPreferredService(in ComponentName service);
     boolean unsetPreferredService();
     boolean supportsAidPrefixRegistration();
+    ApduServiceInfo getPreferredPaymentService(int userHandle);
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 5715c9a..d320f61 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -164,6 +164,18 @@
             "android.nfc.action.TRANSACTION_DETECTED";
 
     /**
+     * Broadcast Action: Intent to notify if the preferred payment service changed.
+     *
+     * <p>This intent will only be sent to the application has requested permission for
+     * {@link android.Manifest.permission#NFC_PREFERRED_PAYMENT_INFO} and if the application
+     * has the necessary access to Secure Element which witnessed the particular event.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PREFERRED_PAYMENT_CHANGED =
+            "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
+
+    /**
      * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
      * @hide
      */
@@ -231,6 +243,17 @@
      */
     public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
 
+    /**
+     * Mandatory String extra field in {@link #ACTION_PREFERRED_PAYMENT_CHANGED}
+     * Indicates the condition when trigger this event.
+     */
+    public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON =
+            "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON";
+
+    public static final int PREFERRED_PAYMENT_LOADED = 1;
+    public static final int PREFERRED_PAYMENT_CHANGED = 2;
+    public static final int PREFERRED_PAYMENT_UPDATED = 3;
+
     public static final int STATE_OFF = 1;
     public static final int STATE_TURNING_ON = 2;
     public static final int STATE_ON = 3;
@@ -1410,7 +1433,7 @@
     /**
      * Enable foreground dispatch to the given Activity.
      *
-     * <p>This will give give priority to the foreground activity when
+     * <p>This will give priority to the foreground activity when
      * dispatching a discovered {@link Tag} to an application.
      *
      * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index aa93611..f1c74a6 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -17,6 +17,7 @@
 package android.nfc.cardemulation;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -657,6 +658,109 @@
     }
 
     /**
+     * Retrieves the registered AIDs for the preferred payment service.
+     *
+     * @return The list of AIDs registered for this category, or null if it couldn't be found.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @Nullable
+    public List<String> getAidsForPreferredPaymentService() {
+        try {
+            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(mContext.getUserId());
+            return (serviceInfo != null ? serviceInfo.getAids() : null);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                ApduServiceInfo serviceInfo =
+                        sService.getPreferredPaymentService(mContext.getUserId());
+                return (serviceInfo != null ? serviceInfo.getAids() : null);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Retrieves the route destination for the preferred payment service.
+     *
+     * @return The route destination secure element name of the preferred payment service.
+     *         HCE payment: "Host"
+     *         OffHost payment: prefix SIM or prefix eSE string.
+     *                          "OffHost" if the payment service does not specify secure element
+     *                          name.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @Nullable
+    public String getRouteDestinationForPreferredPaymentService() {
+        try {
+            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(mContext.getUserId());
+            if (serviceInfo != null) {
+                if (!serviceInfo.isOnHost()) {
+                    return serviceInfo.getOffHostSecureElement() == null ?
+                            "OffHost" : serviceInfo.getOffHostSecureElement();
+                }
+                return "Host";
+            }
+            return null;
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                ApduServiceInfo serviceInfo =
+                        sService.getPreferredPaymentService(mContext.getUserId());
+                if (serviceInfo != null) {
+                    if (!serviceInfo.isOnHost()) {
+                        return serviceInfo.getOffHostSecureElement() == null ?
+                                "Offhost" : serviceInfo.getOffHostSecureElement();
+                    }
+                    return "Host";
+                }
+                return null;
+
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Returns a user-visible description of the preferred payment service.
+     *
+     * @return the preferred payment service description
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @Nullable
+    public String getDescriptionForPreferredPaymentService() {
+        try {
+            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(mContext.getUserId());
+            return (serviceInfo != null ? serviceInfo.getDescription() : null);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                ApduServiceInfo serviceInfo =
+                        sService.getPreferredPaymentService(mContext.getUserId());
+                return (serviceInfo != null ? serviceInfo.getDescription() : null);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
      * @hide
      */
     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2f8e30e..82b04a6 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -20,6 +20,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
@@ -552,6 +553,13 @@
     public static final String REBOOT_SAFE_MODE = "safemode";
 
     /**
+     * The 'reason' value used for rebooting userspace.
+     * @hide
+     */
+    @SystemApi
+    public static final String REBOOT_USERSPACE = "userspace";
+
+    /**
      * The 'reason' value used when rebooting the device without turning on the screen.
      * @hide
      */
@@ -1326,6 +1334,14 @@
     }
 
     /**
+     * Returns {@code true} if this device supports rebooting userspace.
+     */
+    // TODO(b/138605180): add link to documentation once it's ready.
+    public boolean isRebootingUserspaceSupported() {
+        return SystemProperties.getBoolean("ro.init.userspace_reboot.is_supported", false);
+    }
+
+    /**
      * Reboot the device.  Will not return if the reboot is successful.
      * <p>
      * Requires the {@link android.Manifest.permission#REBOOT} permission.
@@ -1333,8 +1349,14 @@
      *
      * @param reason code to pass to the kernel (e.g., "recovery") to
      *               request special boot modes, or null.
+     * @throws UnsupportedOperationException if userspace reboot was requested on a device that
+     *                                       doesn't support it.
      */
-    public void reboot(String reason) {
+    public void reboot(@Nullable String reason) {
+        if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
+            throw new UnsupportedOperationException(
+                    "Attempted userspace reboot on a device that doesn't support it");
+        }
         try {
             mService.reboot(false, reason, true);
         } catch (RemoteException e) {
diff --git a/core/java/android/provider/Column.java b/core/java/android/provider/Column.java
deleted file mode 100644
index 1364fb8..0000000
--- a/core/java/android/provider/Column.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that a field is a {@link ContentProvider} column. It can be used as a
- * key for {@link ContentValues} when inserting or updating data, or as a
- * projection when querying.
- *
- * @hide
- */
-@Documented
-@Retention(RUNTIME)
-@Target({FIELD})
-public @interface Column {
-    /**
-     * The {@link Cursor#getType(int)} of the data stored in this column.
-     */
-    int value();
-
-    /**
-     * This column is read-only and cannot be defined during insert or updates.
-     */
-    boolean readOnly() default false;
-}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
deleted file mode 100644
index 63204d3..0000000
--- a/core/java/android/provider/MediaStore.java
+++ /dev/null
@@ -1,3832 +0,0 @@
-/*
- * Copyright (C) 2007 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.provider;
-
-import android.annotation.BytesLong;
-import android.annotation.CurrentTimeMillisLong;
-import android.annotation.CurrentTimeSecondsLong;
-import android.annotation.DurationMillisLong;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.ClipData;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.UriPermission;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.ImageDecoder;
-import android.graphics.PostProcessor;
-import android.media.ExifInterface;
-import android.media.MediaFormat;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Environment;
-import android.os.OperationCanceledException;
-import android.os.RemoteException;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Size;
-
-import libcore.util.HexEncoding;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * The contract between the media provider and applications. Contains
- * definitions for the supported URIs and columns.
- * <p>
- * The media provider provides an indexed collection of common media types, such
- * as {@link Audio}, {@link Video}, and {@link Images}, from any attached
- * storage devices. Each collection is organized based on the primary MIME type
- * of the underlying content; for example, {@code image/*} content is indexed
- * under {@link Images}. The {@link Files} collection provides a broad view
- * across all collections, and does not filter by MIME type.
- */
-public final class MediaStore {
-    private final static String TAG = "MediaStore";
-
-    /** The authority for the media provider */
-    public static final String AUTHORITY = "media";
-    /** A content:// style uri to the authority for the media provider */
-    public static final @NonNull Uri AUTHORITY_URI =
-            Uri.parse("content://" + AUTHORITY);
-
-    /** @hide */
-    public static final String AUTHORITY_LEGACY = "media_legacy";
-    /** @hide */
-    public static final @NonNull Uri AUTHORITY_LEGACY_URI =
-            Uri.parse("content://" + AUTHORITY_LEGACY);
-
-    /**
-     * Synthetic volume name that provides a view of all content across the
-     * "internal" storage of the device.
-     * <p>
-     * This synthetic volume provides a merged view of all media distributed
-     * with the device, such as built-in ringtones and wallpapers.
-     * <p>
-     * Because this is a synthetic volume, you can't insert new content into
-     * this volume.
-     */
-    public static final String VOLUME_INTERNAL = "internal";
-
-    /**
-     * Synthetic volume name that provides a view of all content across the
-     * "external" storage of the device.
-     * <p>
-     * This synthetic volume provides a merged view of all media across all
-     * currently attached external storage devices.
-     * <p>
-     * Because this is a synthetic volume, you can't insert new content into
-     * this volume. Instead, you can insert content into a specific storage
-     * volume obtained from {@link #getExternalVolumeNames(Context)}.
-     */
-    public static final String VOLUME_EXTERNAL = "external";
-
-    /**
-     * Specific volume name that represents the primary external storage device
-     * at {@link Environment#getExternalStorageDirectory()}.
-     * <p>
-     * This volume may not always be available, such as when the user has
-     * ejected the device. You can find a list of all specific volume names
-     * using {@link #getExternalVolumeNames(Context)}.
-     */
-    public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary";
-
-    /** {@hide} */
-    public static final String WAIT_FOR_IDLE_CALL = "wait_for_idle";
-    /** {@hide} */
-    public static final String SCAN_FILE_CALL = "scan_file";
-    /** {@hide} */
-    public static final String SCAN_VOLUME_CALL = "scan_volume";
-    /** {@hide} */
-    public static final String CREATE_WRITE_REQUEST_CALL = "create_write_request";
-    /** {@hide} */
-    public static final String CREATE_TRASH_REQUEST_CALL = "create_trash_request";
-    /** {@hide} */
-    public static final String CREATE_FAVORITE_REQUEST_CALL = "create_favorite_request";
-    /** {@hide} */
-    public static final String CREATE_DELETE_REQUEST_CALL = "create_delete_request";
-
-
-    /** {@hide} */
-    public static final String GET_VERSION_CALL = "get_version";
-
-    /** {@hide} */
-    public static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
-    /** {@hide} */
-    public static final String GET_MEDIA_URI_CALL = "get_media_uri";
-
-    /** {@hide} */
-    public static final String EXTRA_URI = "uri";
-    /** {@hide} */
-    public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";
-
-    /** {@hide} */
-    public static final String EXTRA_CLIP_DATA = "clip_data";
-    /** {@hide} */
-    public static final String EXTRA_CONTENT_VALUES = "content_values";
-    /** {@hide} */
-    public static final String EXTRA_RESULT = "result";
-
-    /**
-     * This is for internal use by the media scanner only.
-     * Name of the (optional) Uri parameter that determines whether to skip deleting
-     * the file pointed to by the _data column, when deleting the database entry.
-     * The only appropriate value for this parameter is "false", in which case the
-     * delete will be skipped. Note especially that setting this to true, or omitting
-     * the parameter altogether, will perform the default action, which is different
-     * for different types of media.
-     * @hide
-     */
-    public static final String PARAM_DELETE_DATA = "deletedata";
-
-    /** {@hide} */
-    @Deprecated
-    public static final String PARAM_INCLUDE_PENDING = "includePending";
-    /** {@hide} */
-    @Deprecated
-    public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
-    /** {@hide} */
-    public static final String PARAM_PROGRESS = "progress";
-    /** {@hide} */
-    public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
-    /** {@hide} */
-    public static final String PARAM_LIMIT = "limit";
-
-    /**
-     * Activity Action: Launch a music player.
-     * The activity should be able to play, browse, or manipulate music files stored on the device.
-     *
-     * @deprecated Use {@link android.content.Intent#CATEGORY_APP_MUSIC} instead.
-     */
-    @Deprecated
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
-
-    /**
-     * Activity Action: Perform a search for media.
-     * Contains at least the {@link android.app.SearchManager#QUERY} extra.
-     * May also contain any combination of the following extras:
-     * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
-     *
-     * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
-     * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
-     * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
-     * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
-
-    /**
-     * An intent to perform a search for music media and automatically play content from the
-     * result when possible. This can be fired, for example, by the result of a voice recognition
-     * command to listen to music.
-     * <p>This intent always includes the {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS}
-     * and {@link android.app.SearchManager#QUERY} extras. The
-     * {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} extra determines the search mode, and
-     * the value of the {@link android.app.SearchManager#QUERY} extra depends on the search mode.
-     * For more information about the search modes for this intent, see
-     * <a href="{@docRoot}guide/components/intents-common.html#PlaySearch">Play music based
-     * on a search query</a> in <a href="{@docRoot}guide/components/intents-common.html">Common
-     * Intents</a>.</p>
-     *
-     * <p>This intent makes the most sense for apps that can support large-scale search of music,
-     * such as services connected to an online database of music which can be streamed and played
-     * on the device.</p>
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH =
-            "android.media.action.MEDIA_PLAY_FROM_SEARCH";
-
-    /**
-     * An intent to perform a search for readable media and automatically play content from the
-     * result when possible. This can be fired, for example, by the result of a voice recognition
-     * command to read a book or magazine.
-     * <p>
-     * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
-     * contain any type of unstructured text search, like the name of a book or magazine, an author
-     * a genre, a publisher, or any combination of these.
-     * <p>
-     * Because this intent includes an open-ended unstructured search string, it makes the most
-     * sense for apps that can support large-scale search of text media, such as services connected
-     * to an online database of books and/or magazines which can be read on the device.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH =
-            "android.media.action.TEXT_OPEN_FROM_SEARCH";
-
-    /**
-     * An intent to perform a search for video media and automatically play content from the
-     * result when possible. This can be fired, for example, by the result of a voice recognition
-     * command to play movies.
-     * <p>
-     * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
-     * contain any type of unstructured video search, like the name of a movie, one or more actors,
-     * a genre, or any combination of these.
-     * <p>
-     * Because this intent includes an open-ended unstructured search string, it makes the most
-     * sense for apps that can support large-scale search of video, such as services connected to an
-     * online database of videos which can be streamed and played on the device.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH =
-            "android.media.action.VIDEO_PLAY_FROM_SEARCH";
-
-    /**
-     * The name of the Intent-extra used to define the artist
-     */
-    public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
-    /**
-     * The name of the Intent-extra used to define the album
-     */
-    public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
-    /**
-     * The name of the Intent-extra used to define the song title
-     */
-    public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
-    /**
-     * The name of the Intent-extra used to define the genre.
-     */
-    public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre";
-    /**
-     * The name of the Intent-extra used to define the playlist.
-     */
-    public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
-    /**
-     * The name of the Intent-extra used to define the radio channel.
-     */
-    public static final String EXTRA_MEDIA_RADIO_CHANNEL = "android.intent.extra.radio_channel";
-    /**
-     * The name of the Intent-extra used to define the search focus. The search focus
-     * indicates whether the search should be for things related to the artist, album
-     * or song that is identified by the other extras.
-     */
-    public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
-
-    /**
-     * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
-     * This is an int property that overrides the activity's requestedOrientation.
-     * @see android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED
-     */
-    public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
-
-    /**
-     * The name of an Intent-extra used to control the UI of a ViewImage.
-     * This is a boolean property that overrides the activity's default fullscreen state.
-     */
-    public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
-
-    /**
-     * The name of an Intent-extra used to control the UI of a ViewImage.
-     * This is a boolean property that specifies whether or not to show action icons.
-     */
-    public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
-
-    /**
-     * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
-     * This is a boolean property that specifies whether or not to finish the MovieView activity
-     * when the movie completes playing. The default value is true, which means to automatically
-     * exit the movie player activity when the movie completes playing.
-     */
-    public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
-
-    /**
-     * The name of the Intent action used to launch a camera in still image mode.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
-
-    /**
-     * Name under which an activity handling {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or
-     * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE} publishes the service name for its prewarm
-     * service.
-     * <p>
-     * This meta-data should reference the fully qualified class name of the prewarm service
-     * extending {@code CameraPrewarmService}.
-     * <p>
-     * The prewarm service will get bound and receive a prewarm signal
-     * {@code CameraPrewarmService#onPrewarm()} when a camera launch intent fire might be imminent.
-     * An application implementing a prewarm service should do the absolute minimum amount of work
-     * to initialize the camera in order to reduce startup time in likely case that shortly after a
-     * camera launch intent would be sent.
-     */
-    public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE =
-            "android.media.still_image_camera_preview_service";
-
-    /**
-     * The name of the Intent action used to launch a camera in still image mode
-     * for use when the device is secured (e.g. with a pin, password, pattern,
-     * or face unlock). Applications responding to this intent must not expose
-     * any personal content like existing photos or videos on the device. The
-     * applications should be careful not to share any photo or video with other
-     * applications or internet. The activity should use {@link
-     * Activity#setShowWhenLocked} to display
-     * on top of the lock screen while secured. There is no activity stack when
-     * this flag is used, so launching more than one activity is strongly
-     * discouraged.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
-            "android.media.action.STILL_IMAGE_CAMERA_SECURE";
-
-    /**
-     * The name of the Intent action used to launch a camera in video mode.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
-
-    /**
-     * Standard Intent action that can be sent to have the camera application
-     * capture an image and return it.
-     * <p>
-     * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
-     * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
-     * object in the extra field. This is useful for applications that only need a small image.
-     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
-     * value of EXTRA_OUTPUT.
-     * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
-     * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
-     * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
-     * If you don't set a ClipData, it will be copied there for you when calling
-     * {@link Context#startActivity(Intent)}.
-     *
-     * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
-     * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
-     * is not granted, then attempting to use this action will result in a {@link
-     * java.lang.SecurityException}.
-     *
-     *  @see #EXTRA_OUTPUT
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
-
-    /**
-     * Intent action that can be sent to have the camera application capture an image and return
-     * it when the device is secured (e.g. with a pin, password, pattern, or face unlock).
-     * Applications responding to this intent must not expose any personal content like existing
-     * photos or videos on the device. The applications should be careful not to share any photo
-     * or video with other applications or Internet. The activity should use {@link
-     * Activity#setShowWhenLocked} to display on top of the
-     * lock screen while secured. There is no activity stack when this flag is used, so
-     * launching more than one activity is strongly discouraged.
-     * <p>
-     * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
-     * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
-     * object in the extra field. This is useful for applications that only need a small image.
-     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
-     * value of EXTRA_OUTPUT.
-     * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
-     * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
-     * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
-     * If you don't set a ClipData, it will be copied there for you when calling
-     * {@link Context#startActivity(Intent)}.
-     *
-     * @see #ACTION_IMAGE_CAPTURE
-     * @see #EXTRA_OUTPUT
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_IMAGE_CAPTURE_SECURE =
-            "android.media.action.IMAGE_CAPTURE_SECURE";
-
-    /**
-     * Standard Intent action that can be sent to have the camera application
-     * capture a video and return it.
-     * <p>
-     * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
-     * <p>
-     * The caller may pass in an extra EXTRA_OUTPUT to control
-     * where the video is written. If EXTRA_OUTPUT is not present the video will be
-     * written to the standard location for videos, and the Uri of that location will be
-     * returned in the data field of the Uri.
-     * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
-     * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
-     * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
-     * If you don't set a ClipData, it will be copied there for you when calling
-     * {@link Context#startActivity(Intent)}.
-     *
-     * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
-     * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
-     * is not granted, then atempting to use this action will result in a {@link
-     * java.lang.SecurityException}.
-     *
-     * @see #EXTRA_OUTPUT
-     * @see #EXTRA_VIDEO_QUALITY
-     * @see #EXTRA_SIZE_LIMIT
-     * @see #EXTRA_DURATION_LIMIT
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
-
-    /**
-     * Standard action that can be sent to review the given media file.
-     * <p>
-     * The launched application is expected to provide a large-scale view of the
-     * given media file, while allowing the user to quickly access other
-     * recently captured media files.
-     * <p>
-     * Input: {@link Intent#getData} is URI of the primary media item to
-     * initially display.
-     *
-     * @see #ACTION_REVIEW_SECURE
-     * @see #EXTRA_BRIGHTNESS
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public final static String ACTION_REVIEW = "android.provider.action.REVIEW";
-
-    /**
-     * Standard action that can be sent to review the given media file when the
-     * device is secured (e.g. with a pin, password, pattern, or face unlock).
-     * The applications should be careful not to share any media with other
-     * applications or Internet. The activity should use
-     * {@link Activity#setShowWhenLocked} to display on top of the lock screen
-     * while secured. There is no activity stack when this flag is used, so
-     * launching more than one activity is strongly discouraged.
-     * <p>
-     * The launched application is expected to provide a large-scale view of the
-     * given primary media file, while only allowing the user to quickly access
-     * other media from an explicit secondary list.
-     * <p>
-     * Input: {@link Intent#getData} is URI of the primary media item to
-     * initially display. {@link Intent#getClipData} is the limited list of
-     * secondary media items that the user is allowed to review. If
-     * {@link Intent#getClipData} is undefined, then no other media access
-     * should be allowed.
-     *
-     * @see #EXTRA_BRIGHTNESS
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public final static String ACTION_REVIEW_SECURE = "android.provider.action.REVIEW_SECURE";
-
-    /**
-     * When defined, the launched application is requested to set the given
-     * brightness value via
-     * {@link android.view.WindowManager.LayoutParams#screenBrightness} to help
-     * ensure a smooth transition when launching {@link #ACTION_REVIEW} or
-     * {@link #ACTION_REVIEW_SECURE} intents.
-     */
-    public final static String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS";
-
-    /**
-     * The name of the Intent-extra used to control the quality of a recorded video. This is an
-     * integer property. Currently value 0 means low quality, suitable for MMS messages, and
-     * value 1 means high quality. In the future other quality levels may be added.
-     */
-    public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
-
-    /**
-     * Specify the maximum allowed size.
-     */
-    public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
-
-    /**
-     * Specify the maximum allowed recording duration in seconds.
-     */
-    public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
-
-    /**
-     * The name of the Intent-extra used to indicate a content resolver Uri to be used to
-     * store the requested image or video.
-     */
-    public final static String EXTRA_OUTPUT = "output";
-
-    /**
-      * The string that is used when a media attribute is not known. For example,
-      * if an audio file does not have any meta data, the artist and album columns
-      * will be set to this value.
-      */
-    public static final String UNKNOWN_STRING = "<unknown>";
-
-    /**
-     * Specify a {@link Uri} that is "related" to the current operation being
-     * performed.
-     * <p>
-     * This is typically used to allow an operation that may normally be
-     * rejected, such as making a copy of a pre-existing image located under a
-     * {@link MediaColumns#RELATIVE_PATH} where new images are not allowed.
-     * <p>
-     * It's strongly recommended that when making a copy of pre-existing content
-     * that you define the "original document ID" GUID as defined by the <em>XMP
-     * Media Management</em> standard.
-     * <p>
-     * This key can be placed in a {@link Bundle} of extras and passed to
-     * {@link ContentResolver#insert}.
-     */
-    public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri";
-
-    /**
-     * Specify how {@link MediaColumns#IS_PENDING} items should be filtered when
-     * performing a {@link MediaStore} operation.
-     * <p>
-     * This key can be placed in a {@link Bundle} of extras and passed to
-     * {@link ContentResolver#query}, {@link ContentResolver#update}, or
-     * {@link ContentResolver#delete}.
-     * <p>
-     * By default, pending items are filtered away from operations.
-     */
-    @Match
-    public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
-
-    /**
-     * Specify how {@link MediaColumns#IS_TRASHED} items should be filtered when
-     * performing a {@link MediaStore} operation.
-     * <p>
-     * This key can be placed in a {@link Bundle} of extras and passed to
-     * {@link ContentResolver#query}, {@link ContentResolver#update}, or
-     * {@link ContentResolver#delete}.
-     * <p>
-     * By default, trashed items are filtered away from operations.
-     *
-     * @see MediaColumns#IS_TRASHED
-     * @see MediaStore#QUERY_ARG_MATCH_TRASHED
-     * @see MediaStore#createTrashRequest
-     */
-    @Match
-    public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
-
-    /**
-     * Specify how {@link MediaColumns#IS_FAVORITE} items should be filtered
-     * when performing a {@link MediaStore} operation.
-     * <p>
-     * This key can be placed in a {@link Bundle} of extras and passed to
-     * {@link ContentResolver#query}, {@link ContentResolver#update}, or
-     * {@link ContentResolver#delete}.
-     * <p>
-     * By default, favorite items are <em>not</em> filtered away from
-     * operations.
-     *
-     * @see MediaColumns#IS_FAVORITE
-     * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
-     * @see MediaStore#createFavoriteRequest
-     */
-    @Match
-    public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
-
-    /** @hide */
-    @IntDef(flag = true, prefix = { "MATCH_" }, value = {
-            MATCH_DEFAULT,
-            MATCH_INCLUDE,
-            MATCH_EXCLUDE,
-            MATCH_ONLY,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Match {}
-
-    /**
-     * Value indicating that the default matching behavior should be used, as
-     * defined by the key documentation.
-     */
-    public static final int MATCH_DEFAULT = 0;
-
-    /**
-     * Value indicating that operations should include items matching the
-     * criteria defined by this key.
-     * <p>
-     * Note that items <em>not</em> matching the criteria <em>may</em> also be
-     * included depending on the default behavior documented by the key. If you
-     * want to operate exclusively on matching items, use {@link #MATCH_ONLY}.
-     */
-    public static final int MATCH_INCLUDE = 1;
-
-    /**
-     * Value indicating that operations should exclude items matching the
-     * criteria defined by this key.
-     */
-    public static final int MATCH_EXCLUDE = 2;
-
-    /**
-     * Value indicating that operations should only operate on items explicitly
-     * matching the criteria defined by this key.
-     */
-    public static final int MATCH_ONLY = 3;
-
-    /**
-     * Update the given {@link Uri} to also include any pending media items from
-     * calls such as
-     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
-     * By default no pending items are returned.
-     *
-     * @see MediaColumns#IS_PENDING
-     * @see MediaStore#getIncludePending(Uri)
-     * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which
-     *             is more expressive.
-     */
-    @Deprecated
-    public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
-        return setIncludePending(uri.buildUpon()).build();
-    }
-
-    /** @hide */
-    @Deprecated
-    public static @NonNull Uri.Builder setIncludePending(@NonNull Uri.Builder uriBuilder) {
-        return uriBuilder.appendQueryParameter(PARAM_INCLUDE_PENDING, "1");
-    }
-
-    /**
-     * Return if any pending media items should be included in calls such as
-     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
-     *
-     * @see MediaColumns#IS_PENDING
-     * @see MediaStore#setIncludePending(Uri)
-     * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which
-     *             is more expressive.
-     * @removed
-     */
-    @Deprecated
-    public static boolean getIncludePending(@NonNull Uri uri) {
-        return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_INCLUDE_PENDING));
-    }
-
-    /**
-     * Update the given {@link Uri} to also include any trashed media items from
-     * calls such as
-     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
-     * By default no trashed items are returned.
-     *
-     * @see MediaColumns#IS_TRASHED
-     * @see MediaStore#setIncludeTrashed(Uri)
-     * @see MediaStore#trash(Context, Uri)
-     * @see MediaStore#untrash(Context, Uri)
-     * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_TRASHED} which
-     *             is more expressive.
-     * @removed
-     */
-    @Deprecated
-    public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
-        return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
-    }
-
-    /**
-     * Update the given {@link Uri} to indicate that the caller requires the
-     * original file contents when calling
-     * {@link ContentResolver#openFileDescriptor(Uri, String)}.
-     * <p>
-     * This can be useful when the caller wants to ensure they're backing up the
-     * exact bytes of the underlying media, without any Exif redaction being
-     * performed.
-     * <p>
-     * If the original file contents cannot be provided, a
-     * {@link UnsupportedOperationException} will be thrown when the returned
-     * {@link Uri} is used, such as when the caller doesn't hold
-     * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}.
-     *
-     * @see MediaStore#getRequireOriginal(Uri)
-     */
-    public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) {
-        return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build();
-    }
-
-    /**
-     * Return if the caller requires the original file contents when calling
-     * {@link ContentResolver#openFileDescriptor(Uri, String)}.
-     *
-     * @see MediaStore#setRequireOriginal(Uri)
-     */
-    public static boolean getRequireOriginal(@NonNull Uri uri) {
-        return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_REQUIRE_ORIGINAL));
-    }
-
-    /**
-     * Mark the given item as being "trashed", meaning it should be deleted at
-     * some point in the future. This is a more gentle operation than simply
-     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
-     * would take effect immediately.
-     * <p>
-     * This method preserves trashed items for at least 48 hours before erasing
-     * them, giving the user a chance to untrash the item.
-     *
-     * @see MediaColumns#IS_TRASHED
-     * @see MediaStore#setIncludeTrashed(Uri)
-     * @see MediaStore#trash(Context, Uri)
-     * @see MediaStore#untrash(Context, Uri)
-     * @removed
-     */
-    @Deprecated
-    public static void trash(@NonNull Context context, @NonNull Uri uri) {
-        trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
-    }
-
-    /**
-     * Mark the given item as being "trashed", meaning it should be deleted at
-     * some point in the future. This is a more gentle operation than simply
-     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
-     * would take effect immediately.
-     * <p>
-     * This method preserves trashed items for at least the given timeout before
-     * erasing them, giving the user a chance to untrash the item.
-     *
-     * @see MediaColumns#IS_TRASHED
-     * @see MediaStore#setIncludeTrashed(Uri)
-     * @see MediaStore#trash(Context, Uri)
-     * @see MediaStore#untrash(Context, Uri)
-     * @removed
-     */
-    @Deprecated
-    public static void trash(@NonNull Context context, @NonNull Uri uri,
-            @DurationMillisLong long timeoutMillis) {
-        if (timeoutMillis < 0) {
-            throw new IllegalArgumentException();
-        }
-
-        final ContentValues values = new ContentValues();
-        values.put(MediaColumns.IS_TRASHED, 1);
-        values.put(MediaColumns.DATE_EXPIRES,
-                (System.currentTimeMillis() + timeoutMillis) / 1000);
-        context.getContentResolver().update(uri, values, null, null);
-    }
-
-    /**
-     * Mark the given item as being "untrashed", meaning it should no longer be
-     * deleted as previously requested through {@link #trash(Context, Uri)}.
-     *
-     * @see MediaColumns#IS_TRASHED
-     * @see MediaStore#setIncludeTrashed(Uri)
-     * @see MediaStore#trash(Context, Uri)
-     * @see MediaStore#untrash(Context, Uri)
-     * @removed
-     */
-    @Deprecated
-    public static void untrash(@NonNull Context context, @NonNull Uri uri) {
-        final ContentValues values = new ContentValues();
-        values.put(MediaColumns.IS_TRASHED, 0);
-        values.putNull(MediaColumns.DATE_EXPIRES);
-        context.getContentResolver().update(uri, values, null, null);
-    }
-
-    /**
-     * Rewrite the given {@link Uri} to point at
-     * {@link MediaStore#AUTHORITY_LEGACY}.
-     *
-     * @hide
-     */
-    public static @NonNull Uri rewriteToLegacy(@NonNull Uri uri) {
-        return uri.buildUpon().authority(MediaStore.AUTHORITY_LEGACY).build();
-    }
-
-    private static @NonNull PendingIntent createRequest(@NonNull ContentResolver resolver,
-            @NonNull String method, @NonNull Collection<Uri> uris, @Nullable ContentValues values) {
-        Objects.requireNonNull(resolver);
-        Objects.requireNonNull(uris);
-
-        final Iterator<Uri> it = uris.iterator();
-        final ClipData clipData = ClipData.newRawUri(null, it.next());
-        while (it.hasNext()) {
-            clipData.addItem(new ClipData.Item(it.next()));
-        }
-
-        final Bundle extras = new Bundle();
-        extras.putParcelable(EXTRA_CLIP_DATA, clipData);
-        extras.putParcelable(EXTRA_CONTENT_VALUES, values);
-        return resolver.call(AUTHORITY, method, null, extras).getParcelable(EXTRA_RESULT);
-    }
-
-    /**
-     * Create a {@link PendingIntent} that will prompt the user to grant your
-     * app write access for the requested media items.
-     * <p>
-     * This call only generates the request for a prompt; to display the prompt,
-     * call {@link Activity#startIntentSenderForResult} with
-     * {@link PendingIntent#getIntentSender()}. You can then determine if the
-     * user granted your request by testing for {@link Activity#RESULT_OK} in
-     * {@link Activity#onActivityResult}.
-     * <p>
-     * Permissions granted through this mechanism are tied to the lifecycle of
-     * the {@link Activity} that requests them. If you need to retain
-     * longer-term access for background actions, you can place items into a
-     * {@link ClipData} or {@link Intent} which can then be passed to
-     * {@link Context#startService} or
-     * {@link android.app.job.JobInfo.Builder#setClipData}. Be sure to include
-     * any relevant access modes you want to retain, such as
-     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
-     * <p>
-     * The displayed prompt will reflect all the media items you're requesting,
-     * including those for which you already hold write access. If you want to
-     * determine if you already hold write access before requesting access, use
-     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
-     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
-     * <p>
-     * For security and performance reasons this method does not support
-     * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} or
-     * {@link Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
-     *
-     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
-     *            Typically this value is {@link Context#getContentResolver()},
-     *            but if you need more explicit lifecycle controls, you can
-     *            obtain a {@link ContentProviderClient} and wrap it using
-     *            {@link ContentResolver#wrap(ContentProviderClient)}.
-     * @param uris The set of media items to include in this request. Each item
-     *            must be hosted by {@link MediaStore#AUTHORITY} and must
-     *            reference a specific media item by {@link BaseColumns#_ID}.
-     */
-    public static @NonNull PendingIntent createWriteRequest(@NonNull ContentResolver resolver,
-            @NonNull Collection<Uri> uris) {
-        return createRequest(resolver, CREATE_WRITE_REQUEST_CALL, uris, null);
-    }
-
-    /**
-     * Create a {@link PendingIntent} that will prompt the user to trash the
-     * requested media items. When the user approves this request,
-     * {@link MediaColumns#IS_TRASHED} is set on these items.
-     * <p>
-     * This call only generates the request for a prompt; to display the prompt,
-     * call {@link Activity#startIntentSenderForResult} with
-     * {@link PendingIntent#getIntentSender()}. You can then determine if the
-     * user granted your request by testing for {@link Activity#RESULT_OK} in
-     * {@link Activity#onActivityResult}.
-     * <p>
-     * The displayed prompt will reflect all the media items you're requesting,
-     * including those for which you already hold write access. If you want to
-     * determine if you already hold write access before requesting access, use
-     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
-     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
-     *
-     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
-     *            Typically this value is {@link Context#getContentResolver()},
-     *            but if you need more explicit lifecycle controls, you can
-     *            obtain a {@link ContentProviderClient} and wrap it using
-     *            {@link ContentResolver#wrap(ContentProviderClient)}.
-     * @param uris The set of media items to include in this request. Each item
-     *            must be hosted by {@link MediaStore#AUTHORITY} and must
-     *            reference a specific media item by {@link BaseColumns#_ID}.
-     * @param value The {@link MediaColumns#IS_TRASHED} value to apply.
-     * @see MediaColumns#IS_TRASHED
-     * @see MediaStore#QUERY_ARG_MATCH_TRASHED
-     */
-    public static @NonNull PendingIntent createTrashRequest(@NonNull ContentResolver resolver,
-            @NonNull Collection<Uri> uris, boolean value) {
-        final ContentValues values = new ContentValues();
-        if (value) {
-            values.put(MediaColumns.IS_TRASHED, 1);
-            values.put(MediaColumns.DATE_EXPIRES,
-                    (System.currentTimeMillis() + DateUtils.WEEK_IN_MILLIS) / 1000);
-        } else {
-            values.put(MediaColumns.IS_TRASHED, 0);
-            values.putNull(MediaColumns.DATE_EXPIRES);
-        }
-        return createRequest(resolver, CREATE_TRASH_REQUEST_CALL, uris, values);
-    }
-
-    /**
-     * Create a {@link PendingIntent} that will prompt the user to favorite the
-     * requested media items. When the user approves this request,
-     * {@link MediaColumns#IS_FAVORITE} is set on these items.
-     * <p>
-     * This call only generates the request for a prompt; to display the prompt,
-     * call {@link Activity#startIntentSenderForResult} with
-     * {@link PendingIntent#getIntentSender()}. You can then determine if the
-     * user granted your request by testing for {@link Activity#RESULT_OK} in
-     * {@link Activity#onActivityResult}.
-     * <p>
-     * The displayed prompt will reflect all the media items you're requesting,
-     * including those for which you already hold write access. If you want to
-     * determine if you already hold write access before requesting access, use
-     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
-     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
-     *
-     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
-     *            Typically this value is {@link Context#getContentResolver()},
-     *            but if you need more explicit lifecycle controls, you can
-     *            obtain a {@link ContentProviderClient} and wrap it using
-     *            {@link ContentResolver#wrap(ContentProviderClient)}.
-     * @param uris The set of media items to include in this request. Each item
-     *            must be hosted by {@link MediaStore#AUTHORITY} and must
-     *            reference a specific media item by {@link BaseColumns#_ID}.
-     * @param value The {@link MediaColumns#IS_FAVORITE} value to apply.
-     * @see MediaColumns#IS_FAVORITE
-     * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
-     */
-    public static @NonNull PendingIntent createFavoriteRequest(@NonNull ContentResolver resolver,
-            @NonNull Collection<Uri> uris, boolean value) {
-        final ContentValues values = new ContentValues();
-        if (value) {
-            values.put(MediaColumns.IS_FAVORITE, 1);
-        } else {
-            values.put(MediaColumns.IS_FAVORITE, 0);
-        }
-        return createRequest(resolver, CREATE_FAVORITE_REQUEST_CALL, uris, values);
-    }
-
-    /**
-     * Create a {@link PendingIntent} that will prompt the user to permanently
-     * delete the requested media items. When the user approves this request,
-     * {@link ContentResolver#delete} will be called on these items.
-     * <p>
-     * This call only generates the request for a prompt; to display the prompt,
-     * call {@link Activity#startIntentSenderForResult} with
-     * {@link PendingIntent#getIntentSender()}. You can then determine if the
-     * user granted your request by testing for {@link Activity#RESULT_OK} in
-     * {@link Activity#onActivityResult}.
-     * <p>
-     * The displayed prompt will reflect all the media items you're requesting,
-     * including those for which you already hold write access. If you want to
-     * determine if you already hold write access before requesting access, use
-     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
-     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
-     *
-     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
-     *            Typically this value is {@link Context#getContentResolver()},
-     *            but if you need more explicit lifecycle controls, you can
-     *            obtain a {@link ContentProviderClient} and wrap it using
-     *            {@link ContentResolver#wrap(ContentProviderClient)}.
-     * @param uris The set of media items to include in this request. Each item
-     *            must be hosted by {@link MediaStore#AUTHORITY} and must
-     *            reference a specific media item by {@link BaseColumns#_ID}.
-     */
-    public static @NonNull PendingIntent createDeleteRequest(@NonNull ContentResolver resolver,
-            @NonNull Collection<Uri> uris) {
-        return createRequest(resolver, CREATE_DELETE_REQUEST_CALL, uris, null);
-    }
-
-    /**
-     * Common media metadata columns.
-     */
-    public interface MediaColumns extends BaseColumns {
-        /**
-         * Absolute filesystem path to the media item on disk.
-         * <p>
-         * Note that apps may not have filesystem permissions to directly access
-         * this path. Instead of trying to open this path directly, apps should
-         * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
-         * access.
-         *
-         * @deprecated Apps may not have filesystem permissions to directly
-         *             access this path. Instead of trying to open this path
-         *             directly, apps should use
-         *             {@link ContentResolver#openFileDescriptor(Uri, String)}
-         *             to gain access.
-         */
-        @Deprecated
-        @Column(Cursor.FIELD_TYPE_STRING)
-        public static final String DATA = "_data";
-
-        /**
-         * Indexed value of {@link File#length()} extracted from this media
-         * item.
-         */
-        @BytesLong
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String SIZE = "_size";
-
-        /**
-         * The display name of the media item.
-         * <p>
-         * For example, an item stored at
-         * {@code /storage/0000-0000/DCIM/Vacation/IMG1024.JPG} would have a
-         * display name of {@code IMG1024.JPG}.
-         */
-        @Column(Cursor.FIELD_TYPE_STRING)
-        public static final String DISPLAY_NAME = "_display_name";
-
-        /**
-         * The time the media item was first added.
-         */
-        @CurrentTimeSecondsLong
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String DATE_ADDED = "date_added";
-
-        /**
-         * Indexed value of {@link File#lastModified()} extracted from this
-         * media item.
-         */
-        @CurrentTimeSecondsLong
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String DATE_MODIFIED = "date_modified";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DATE} or
-         * {@link ExifInterface#TAG_DATETIME_ORIGINAL} extracted from this media
-         * item.
-         * <p>
-         * Note that images must define both
-         * {@link ExifInterface#TAG_DATETIME_ORIGINAL} and
-         * {@code ExifInterface#TAG_OFFSET_TIME_ORIGINAL} to reliably determine
-         * this value in relation to the epoch.
-         */
-        @CurrentTimeMillisLong
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String DATE_TAKEN = "datetaken";
-
-        /**
-         * The MIME type of the media item.
-         * <p>
-         * This is typically defined based on the file extension of the media
-         * item. However, it may be the value of the {@code format} attribute
-         * defined by the <em>Dublin Core Media Initiative</em> standard,
-         * extracted from any XMP metadata contained within this media item.
-         * <p class="note">
-         * Note: the {@code format} attribute may be ignored if the top-level
-         * MIME type disagrees with the file extension. For example, it's
-         * reasonable for an {@code image/jpeg} file to declare a {@code format}
-         * of {@code image/vnd.google.panorama360+jpg}, but declaring a
-         * {@code format} of {@code audio/ogg} would be ignored.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         */
-        @Column(Cursor.FIELD_TYPE_STRING)
-        public static final String MIME_TYPE = "mime_type";
-
-        /**
-         * Non-zero if the media file is drm-protected
-         * @hide
-         */
-        @UnsupportedAppUsage
-        @Deprecated
-        @Column(Cursor.FIELD_TYPE_INTEGER)
-        public static final String IS_DRM = "is_drm";
-
-        /**
-         * Flag indicating if a media item is pending, and still being inserted
-         * by its owner. While this flag is set, only the owner of the item can
-         * open the underlying file; requests from other apps will be rejected.
-         * <p>
-         * Pending items are retained either until they are published by setting
-         * the field to {@code 0}, or until they expire as defined by
-         * {@link #DATE_EXPIRES}.
-         *
-         * @see MediaStore#QUERY_ARG_MATCH_PENDING
-         */
-        @Column(Cursor.FIELD_TYPE_INTEGER)
-        public static final String IS_PENDING = "is_pending";
-
-        /**
-         * Flag indicating if a media item is trashed.
-         * <p>
-         * Trashed items are retained until they expire as defined by
-         * {@link #DATE_EXPIRES}.
-         *
-         * @see MediaColumns#IS_TRASHED
-         * @see MediaStore#QUERY_ARG_MATCH_TRASHED
-         * @see MediaStore#createTrashRequest
-         */
-        @Column(Cursor.FIELD_TYPE_INTEGER)
-        public static final String IS_TRASHED = "is_trashed";
-
-        /**
-         * The time the media item should be considered expired. Typically only
-         * meaningful in the context of {@link #IS_PENDING} or
-         * {@link #IS_TRASHED}.
-         */
-        @CurrentTimeSecondsLong
-        @Column(Cursor.FIELD_TYPE_INTEGER)
-        public static final String DATE_EXPIRES = "date_expires";
-
-        /**
-         * Indexed value of
-         * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_WIDTH},
-         * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_WIDTH} or
-         * {@link ExifInterface#TAG_IMAGE_WIDTH} extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String WIDTH = "width";
-
-        /**
-         * Indexed value of
-         * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_HEIGHT},
-         * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_HEIGHT} or
-         * {@link ExifInterface#TAG_IMAGE_LENGTH} extracted from this media
-         * item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String HEIGHT = "height";
-
-        /**
-         * Calculated value that combines {@link #WIDTH} and {@link #HEIGHT}
-         * into a user-presentable string.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String RESOLUTION = "resolution";
-
-        /**
-         * Package name that contributed this media. The value may be
-         * {@code NULL} if ownership cannot be reliably determined.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String OWNER_PACKAGE_NAME = "owner_package_name";
-
-        /**
-         * Volume name of the specific storage device where this media item is
-         * persisted. The value is typically one of the volume names returned
-         * from {@link MediaStore#getExternalVolumeNames(Context)}.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String VOLUME_NAME = "volume_name";
-
-        /**
-         * Relative path of this media item within the storage device where it
-         * is persisted. For example, an item stored at
-         * {@code /storage/0000-0000/DCIM/Vacation/IMG1024.JPG} would have a
-         * path of {@code DCIM/Vacation/}.
-         * <p>
-         * This value should only be used for organizational purposes, and you
-         * should not attempt to construct or access a raw filesystem path using
-         * this value. If you need to open a media item, use an API like
-         * {@link ContentResolver#openFileDescriptor(Uri, String)}.
-         * <p>
-         * When this value is set to {@code NULL} during an
-         * {@link ContentResolver#insert} operation, the newly created item will
-         * be placed in a relevant default location based on the type of media
-         * being inserted. For example, a {@code image/jpeg} item will be placed
-         * under {@link Environment#DIRECTORY_PICTURES}.
-         * <p>
-         * You can modify this column during an {@link ContentResolver#update}
-         * call, which will move the underlying file on disk.
-         * <p>
-         * In both cases above, content must be placed under a top-level
-         * directory that is relevant to the media type. For example, attempting
-         * to place a {@code audio/mpeg} file under
-         * {@link Environment#DIRECTORY_PICTURES} will be rejected.
-         */
-        @Column(Cursor.FIELD_TYPE_STRING)
-        public static final String RELATIVE_PATH = "relative_path";
-
-        /**
-         * The primary bucket ID of this media item. This can be useful to
-         * present the user a first-level clustering of related media items.
-         * This is a read-only column that is automatically computed.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String BUCKET_ID = "bucket_id";
-
-        /**
-         * The primary bucket display name of this media item. This can be
-         * useful to present the user a first-level clustering of related
-         * media items. This is a read-only column that is automatically
-         * computed.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
-
-        /**
-         * The group ID of this media item. This can be useful to present
-         * the user a grouping of related media items, such a burst of
-         * images, or a {@code JPG} and {@code DNG} version of the same
-         * image.
-         * <p>
-         * This is a read-only column that is automatically computed based
-         * on the first portion of the filename. For example,
-         * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
-         * will have the same {@link #GROUP_ID} because the first portion of
-         * their filenames is identical.
-         *
-         * @removed
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        @Deprecated
-        public static final String GROUP_ID = "group_id";
-
-        /**
-         * The "document ID" GUID as defined by the <em>XMP Media
-         * Management</em> standard, extracted from any XMP metadata contained
-         * within this media item. The value is {@code null} when no metadata
-         * was found.
-         * <p>
-         * Each "document ID" is created once for each new resource. Different
-         * renditions of that resource are expected to have different IDs.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String DOCUMENT_ID = "document_id";
-
-        /**
-         * The "instance ID" GUID as defined by the <em>XMP Media
-         * Management</em> standard, extracted from any XMP metadata contained
-         * within this media item. The value is {@code null} when no metadata
-         * was found.
-         * <p>
-         * This "instance ID" changes with each save operation of a specific
-         * "document ID".
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String INSTANCE_ID = "instance_id";
-
-        /**
-         * The "original document ID" GUID as defined by the <em>XMP Media
-         * Management</em> standard, extracted from any XMP metadata contained
-         * within this media item.
-         * <p>
-         * This "original document ID" links a resource to its original source.
-         * For example, when you save a PSD document as a JPEG, then convert the
-         * JPEG to GIF format, the "original document ID" of both the JPEG and
-         * GIF files is the "document ID" of the original PSD file.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
-
-        /**
-         * Indexed value of
-         * {@link MediaMetadataRetriever#METADATA_KEY_VIDEO_ROTATION},
-         * {@link MediaMetadataRetriever#METADATA_KEY_IMAGE_ROTATION}, or
-         * {@link ExifInterface#TAG_ORIENTATION} extracted from this media item.
-         * <p>
-         * For consistency the indexed value is expressed in degrees, such as 0,
-         * 90, 180, or 270.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String ORIENTATION = "orientation";
-
-        /**
-         * Flag indicating if the media item has been marked as being a
-         * "favorite" by the user.
-         *
-         * @see MediaColumns#IS_FAVORITE
-         * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
-         * @see MediaStore#createFavoriteRequest
-         */
-        @Column(Cursor.FIELD_TYPE_INTEGER)
-        public static final String IS_FAVORITE = "is_favorite";
-
-        // =======================================
-        // ==== MediaMetadataRetriever values ====
-        // =======================================
-
-        /**
-         * Indexed value of
-         * {@link MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER} extracted
-         * from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String CD_TRACK_NUMBER = "cd_track_number";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ALBUM}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String ALBUM = "album";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ARTIST}
-         * or {@link ExifInterface#TAG_ARTIST} extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String ARTIST = "artist";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_AUTHOR}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String AUTHOR = "author";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_COMPOSER}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String COMPOSER = "composer";
-
-        // METADATA_KEY_DATE is DATE_TAKEN
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_GENRE}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String GENRE = "genre";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_TITLE}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String TITLE = "title";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_YEAR}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String YEAR = "year";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DURATION}
-         * extracted from this media item.
-         */
-        @DurationMillisLong
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String DURATION = "duration";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_NUM_TRACKS}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String NUM_TRACKS = "num_tracks";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_WRITER}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String WRITER = "writer";
-
-        // METADATA_KEY_MIMETYPE is MIME_TYPE
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String ALBUM_ARTIST = "album_artist";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String DISC_NUMBER = "disc_number";
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_COMPILATION}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-        public static final String COMPILATION = "compilation";
-
-        // HAS_AUDIO is ignored
-        // HAS_VIDEO is ignored
-        // VIDEO_WIDTH is WIDTH
-        // VIDEO_HEIGHT is HEIGHT
-
-        /**
-         * Indexed value of {@link MediaMetadataRetriever#METADATA_KEY_BITRATE}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-        public static final String BITRATE = "bitrate";
-
-        // TIMED_TEXT_LANGUAGES is ignored
-        // IS_DRM is ignored
-        // LOCATION is LATITUDE and LONGITUDE
-        // VIDEO_ROTATION is ORIENTATION
-
-        /**
-         * Indexed value of
-         * {@link MediaMetadataRetriever#METADATA_KEY_CAPTURE_FRAMERATE}
-         * extracted from this media item.
-         */
-        @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
-        public static final String CAPTURE_FRAMERATE = "capture_framerate";
-
-        // HAS_IMAGE is ignored
-        // IMAGE_COUNT is ignored
-        // IMAGE_PRIMARY is ignored
-        // IMAGE_WIDTH is WIDTH
-        // IMAGE_HEIGHT is HEIGHT
-        // IMAGE_ROTATION is ORIENTATION
-        // VIDEO_FRAME_COUNT is ignored
-        // EXIF_OFFSET is ignored
-        // EXIF_LENGTH is ignored
-        // COLOR_STANDARD is ignored
-        // COLOR_TRANSFER is ignored
-        // COLOR_RANGE is ignored
-        // SAMPLERATE is ignored
-        // BITS_PER_SAMPLE is ignored
-    }
-
-    /**
-     * Media provider table containing an index of all files in the media storage,
-     * including non-media files.  This should be used by applications that work with
-     * non-media file types (text, HTML, PDF, etc) as well as applications that need to
-     * work with multiple media file types in a single query.
-     */
-    public static final class Files {
-        /** @hide */
-        public static final String TABLE = "files";
-
-        /** @hide */
-        public static final Uri EXTERNAL_CONTENT_URI = getContentUri(VOLUME_EXTERNAL);
-
-        /**
-         * Get the content:// style URI for the files table on the
-         * given volume.
-         *
-         * @param volumeName the name of the volume to get the URI for
-         * @return the URI to the files table on the given volume
-         */
-        public static Uri getContentUri(String volumeName) {
-            return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("file").build();
-        }
-
-        /**
-         * Get the content:// style URI for a single row in the files table on the
-         * given volume.
-         *
-         * @param volumeName the name of the volume to get the URI for
-         * @param rowId the file to get the URI for
-         * @return the URI to the files table on the given volume
-         */
-        public static final Uri getContentUri(String volumeName,
-                long rowId) {
-            return ContentUris.withAppendedId(getContentUri(volumeName), rowId);
-        }
-
-        /** {@hide} */
-        @UnsupportedAppUsage
-        public static Uri getMtpObjectsUri(@NonNull String volumeName) {
-            return MediaStore.Files.getContentUri(volumeName);
-        }
-
-        /** {@hide} */
-        @UnsupportedAppUsage
-        public static final Uri getMtpObjectsUri(@NonNull String volumeName, long fileId) {
-            return MediaStore.Files.getContentUri(volumeName, fileId);
-        }
-
-        /** {@hide} */
-        @UnsupportedAppUsage
-        public static final Uri getMtpReferencesUri(@NonNull String volumeName, long fileId) {
-            return MediaStore.Files.getContentUri(volumeName, fileId);
-        }
-
-        /**
-         * Used to trigger special logic for directories.
-         * @hide
-         */
-        public static final Uri getDirectoryUri(String volumeName) {
-            return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("dir").build();
-        }
-
-        /** @hide */
-        public static final Uri getContentUriForPath(String path) {
-            return getContentUri(getVolumeName(new File(path)));
-        }
-
-        /**
-         * File metadata columns.
-         */
-        public interface FileColumns extends MediaColumns {
-            /**
-             * The MTP storage ID of the file
-             * @hide
-             */
-            @UnsupportedAppUsage
-            @Deprecated
-            // @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String STORAGE_ID = "storage_id";
-
-            /**
-             * The MTP format code of the file
-             * @hide
-             */
-            @UnsupportedAppUsage
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String FORMAT = "format";
-
-            /**
-             * The index of the parent directory of the file
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String PARENT = "parent";
-
-            /**
-             * The MIME type of the media item.
-             * <p>
-             * This is typically defined based on the file extension of the media
-             * item. However, it may be the value of the {@code format} attribute
-             * defined by the <em>Dublin Core Media Initiative</em> standard,
-             * extracted from any XMP metadata contained within this media item.
-             * <p class="note">
-             * Note: the {@code format} attribute may be ignored if the top-level
-             * MIME type disagrees with the file extension. For example, it's
-             * reasonable for an {@code image/jpeg} file to declare a {@code format}
-             * of {@code image/vnd.google.panorama360+jpg}, but declaring a
-             * {@code format} of {@code audio/ogg} would be ignored.
-             * <p>
-             * This is a read-only column that is automatically computed.
-             */
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String MIME_TYPE = "mime_type";
-
-            /** @removed promoted to parent interface */
-            public static final String TITLE = "title";
-
-            /**
-             * The media type (audio, video, image or playlist)
-             * of the file, or 0 for not a media file
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String MEDIA_TYPE = "media_type";
-
-            /**
-             * Constant for the {@link #MEDIA_TYPE} column indicating that file
-             * is not an audio, image, video, playlist, or subtitles file.
-             */
-            public static final int MEDIA_TYPE_NONE = 0;
-
-            /**
-             * Constant for the {@link #MEDIA_TYPE} column indicating that file
-             * is an image file.
-             */
-            public static final int MEDIA_TYPE_IMAGE = 1;
-
-            /**
-             * Constant for the {@link #MEDIA_TYPE} column indicating that file
-             * is an audio file.
-             */
-            public static final int MEDIA_TYPE_AUDIO = 2;
-
-            /**
-             * Constant for the {@link #MEDIA_TYPE} column indicating that file
-             * is a video file.
-             */
-            public static final int MEDIA_TYPE_VIDEO = 3;
-
-            /**
-             * Constant for the {@link #MEDIA_TYPE} column indicating that file
-             * is a playlist file.
-             */
-            public static final int MEDIA_TYPE_PLAYLIST = 4;
-
-            /**
-             * Constant for the {@link #MEDIA_TYPE} column indicating that file
-             * is a subtitles or lyrics file.
-             */
-            public static final int MEDIA_TYPE_SUBTITLE = 5;
-
-            /**
-             * Column indicating if the file is part of Downloads collection.
-             * @hide
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_DOWNLOAD = "is_download";
-        }
-    }
-
-    /** @hide */
-    public static class ThumbnailConstants {
-        public static final int MINI_KIND = 1;
-        public static final int FULL_SCREEN_KIND = 2;
-        public static final int MICRO_KIND = 3;
-
-        public static final Size MINI_SIZE = new Size(512, 384);
-        public static final Size FULL_SCREEN_SIZE = new Size(1024, 786);
-        public static final Size MICRO_SIZE = new Size(96, 96);
-
-        public static @NonNull Size getKindSize(int kind) {
-            if (kind == ThumbnailConstants.MICRO_KIND) {
-                return ThumbnailConstants.MICRO_SIZE;
-            } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
-                return ThumbnailConstants.FULL_SCREEN_SIZE;
-            } else if (kind == ThumbnailConstants.MINI_KIND) {
-                return ThumbnailConstants.MINI_SIZE;
-            } else {
-                throw new IllegalArgumentException("Unsupported kind: " + kind);
-            }
-        }
-    }
-
-    /**
-     * Download metadata columns.
-     */
-    public interface DownloadColumns extends MediaColumns {
-        /**
-         * Uri indicating where the item has been downloaded from.
-         */
-        @Column(Cursor.FIELD_TYPE_STRING)
-        String DOWNLOAD_URI = "download_uri";
-
-        /**
-         * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
-         */
-        @Column(Cursor.FIELD_TYPE_STRING)
-        String REFERER_URI = "referer_uri";
-
-        /**
-         * The description of the download.
-         *
-         * @removed
-         */
-        @Deprecated
-        @Column(Cursor.FIELD_TYPE_STRING)
-        String DESCRIPTION = "description";
-    }
-
-    /**
-     * Collection of downloaded items.
-     */
-    public static final class Downloads implements DownloadColumns {
-        private Downloads() {}
-
-        /**
-         * The content:// style URI for the internal storage.
-         */
-        @NonNull
-        public static final Uri INTERNAL_CONTENT_URI =
-                getContentUri("internal");
-
-        /**
-         * The content:// style URI for the "primary" external storage
-         * volume.
-         */
-        @NonNull
-        public static final Uri EXTERNAL_CONTENT_URI =
-                getContentUri("external");
-
-        /**
-         * The MIME type for this table.
-         */
-        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/download";
-
-        /**
-         * Regex that matches paths that needs to be considered part of downloads collection.
-         * @hide
-         */
-        public static final Pattern PATTERN_DOWNLOADS_FILE = Pattern.compile(
-                "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/.+");
-        private static final Pattern PATTERN_DOWNLOADS_DIRECTORY = Pattern.compile(
-                "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/?");
-
-        /**
-         * Get the content:// style URI for the downloads table on the
-         * given volume.
-         *
-         * @param volumeName the name of the volume to get the URI for
-         * @return the URI to the image media table on the given volume
-         */
-        public static @NonNull Uri getContentUri(@NonNull String volumeName) {
-            return AUTHORITY_URI.buildUpon().appendPath(volumeName)
-                    .appendPath("downloads").build();
-        }
-
-        /**
-         * Get the content:// style URI for a single row in the downloads table
-         * on the given volume.
-         *
-         * @param volumeName the name of the volume to get the URI for
-         * @param id the download to get the URI for
-         * @return the URI to the downloads table on the given volume
-         */
-        public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
-            return ContentUris.withAppendedId(getContentUri(volumeName), id);
-        }
-
-        /** @hide */
-        public static @NonNull Uri getContentUriForPath(@NonNull String path) {
-            return getContentUri(getVolumeName(new File(path)));
-        }
-
-        /** @hide */
-        public static boolean isDownload(@NonNull String path) {
-            return PATTERN_DOWNLOADS_FILE.matcher(path).matches();
-        }
-
-        /** @hide */
-        public static boolean isDownloadDir(@NonNull String path) {
-            return PATTERN_DOWNLOADS_DIRECTORY.matcher(path).matches();
-        }
-    }
-
-    /**
-     * @deprecated since this method doesn't have a {@link Context}, we can't
-     *             find the actual {@link StorageVolume} for the given path, so
-     *             only a vague guess is returned. Callers should use
-     *             {@link StorageManager#getStorageVolume(File)} instead.
-     * @hide
-     */
-    @Deprecated
-    public static @NonNull String getVolumeName(@NonNull File path) {
-        // Ideally we'd find the relevant StorageVolume, but we don't have a
-        // Context to obtain it from, so the best we can do is assume
-        if (path.getAbsolutePath()
-                .startsWith(Environment.getStorageDirectory().getAbsolutePath())) {
-            return MediaStore.VOLUME_EXTERNAL;
-        } else {
-            return MediaStore.VOLUME_INTERNAL;
-        }
-    }
-
-    /**
-     * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
-     * to be accessed elsewhere.
-     */
-    @Deprecated
-    private static class InternalThumbnails implements BaseColumns {
-        /**
-         * Currently outstanding thumbnail requests that can be cancelled.
-         */
-        // @GuardedBy("sPending")
-        private static ArrayMap<Uri, CancellationSignal> sPending = new ArrayMap<>();
-
-        /**
-         * Make a blocking request to obtain the given thumbnail, generating it
-         * if needed.
-         *
-         * @see #cancelThumbnail(ContentResolver, Uri)
-         */
-        @Deprecated
-        static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
-                int kind, @Nullable BitmapFactory.Options opts) {
-            final Size size = ThumbnailConstants.getKindSize(kind);
-
-            CancellationSignal signal = null;
-            synchronized (sPending) {
-                signal = sPending.get(uri);
-                if (signal == null) {
-                    signal = new CancellationSignal();
-                    sPending.put(uri, signal);
-                }
-            }
-
-            try {
-                return cr.loadThumbnail(uri, size, signal);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
-                return null;
-            } finally {
-                synchronized (sPending) {
-                    sPending.remove(uri);
-                }
-            }
-        }
-
-        /**
-         * This method cancels the thumbnail request so clients waiting for
-         * {@link #getThumbnail} will be interrupted and return immediately.
-         * Only the original process which made the request can cancel their own
-         * requests.
-         */
-        @Deprecated
-        static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
-            synchronized (sPending) {
-                final CancellationSignal signal = sPending.get(uri);
-                if (signal != null) {
-                    signal.cancel();
-                }
-            }
-        }
-    }
-
-    /**
-     * Collection of all media with MIME type of {@code image/*}.
-     */
-    public static final class Images {
-        /**
-         * Image metadata columns.
-         */
-        public interface ImageColumns extends MediaColumns {
-            /**
-             * The picasa id of the image
-             *
-             * @deprecated this value was only relevant for images hosted on
-             *             Picasa, which are no longer supported.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String PICASA_ID = "picasa_id";
-
-            /**
-             * Whether the video should be published as public or private
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String IS_PRIVATE = "isprivate";
-
-            /**
-             * The latitude where the image was captured.
-             *
-             * @deprecated location details are no longer indexed for privacy
-             *             reasons, and this value is now always {@code null}.
-             *             You can still manually obtain location metadata using
-             *             {@link ExifInterface#getLatLong(float[])}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
-            public static final String LATITUDE = "latitude";
-
-            /**
-             * The longitude where the image was captured.
-             *
-             * @deprecated location details are no longer indexed for privacy
-             *             reasons, and this value is now always {@code null}.
-             *             You can still manually obtain location metadata using
-             *             {@link ExifInterface#getLatLong(float[])}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
-            public static final String LONGITUDE = "longitude";
-
-            /** @removed promoted to parent interface */
-            public static final String DATE_TAKEN = "datetaken";
-            /** @removed promoted to parent interface */
-            public static final String ORIENTATION = "orientation";
-
-            /**
-             * The mini thumb id.
-             *
-             * @deprecated all thumbnails should be obtained via
-             *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
-             *             value is no longer supported.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
-
-            /** @removed promoted to parent interface */
-            public static final String BUCKET_ID = "bucket_id";
-            /** @removed promoted to parent interface */
-            public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
-            /** @removed promoted to parent interface */
-            public static final String GROUP_ID = "group_id";
-
-            /**
-             * Indexed value of {@link ExifInterface#TAG_IMAGE_DESCRIPTION}
-             * extracted from this media item.
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String DESCRIPTION = "description";
-
-            /**
-             * Indexed value of {@link ExifInterface#TAG_EXPOSURE_TIME}
-             * extracted from this media item.
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String EXPOSURE_TIME = "exposure_time";
-
-            /**
-             * Indexed value of {@link ExifInterface#TAG_F_NUMBER}
-             * extracted from this media item.
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String F_NUMBER = "f_number";
-
-            /**
-             * Indexed value of {@link ExifInterface#TAG_ISO_SPEED_RATINGS}
-             * extracted from this media item.
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String ISO = "iso";
-        }
-
-        public static final class Media implements ImageColumns {
-            /**
-             * @deprecated all queries should be performed through
-             *             {@link ContentResolver} directly, which offers modern
-             *             features like {@link CancellationSignal}.
-             */
-            @Deprecated
-            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
-                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
-            }
-
-            /**
-             * @deprecated all queries should be performed through
-             *             {@link ContentResolver} directly, which offers modern
-             *             features like {@link CancellationSignal}.
-             */
-            @Deprecated
-            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
-                    String where, String orderBy) {
-                return cr.query(uri, projection, where,
-                                             null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-            }
-
-            /**
-             * @deprecated all queries should be performed through
-             *             {@link ContentResolver} directly, which offers modern
-             *             features like {@link CancellationSignal}.
-             */
-            @Deprecated
-            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
-                    String selection, String [] selectionArgs, String orderBy) {
-                return cr.query(uri, projection, selection,
-                        selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-            }
-
-            /**
-             * Retrieves an image for the given url as a {@link Bitmap}.
-             *
-             * @param cr The content resolver to use
-             * @param url The url of the image
-             * @deprecated loading of images should be performed through
-             *             {@link ImageDecoder#createSource(ContentResolver, Uri)},
-             *             which offers modern features like
-             *             {@link PostProcessor}.
-             */
-            @Deprecated
-            public static final Bitmap getBitmap(ContentResolver cr, Uri url)
-                    throws FileNotFoundException, IOException {
-                InputStream input = cr.openInputStream(url);
-                Bitmap bitmap = BitmapFactory.decodeStream(input);
-                input.close();
-                return bitmap;
-            }
-
-            /**
-             * Insert an image and create a thumbnail for it.
-             *
-             * @param cr The content resolver to use
-             * @param imagePath The path to the image to insert
-             * @param name The name of the image
-             * @param description The description of the image
-             * @return The URL to the newly created image
-             * @deprecated inserting of images should be performed using
-             *             {@link MediaColumns#IS_PENDING}, which offers richer
-             *             control over lifecycle.
-             */
-            @Deprecated
-            public static final String insertImage(ContentResolver cr, String imagePath,
-                    String name, String description) throws FileNotFoundException {
-                final Bitmap source;
-                try {
-                    source = ImageDecoder
-                            .decodeBitmap(ImageDecoder.createSource(new File(imagePath)));
-                } catch (IOException e) {
-                    throw new FileNotFoundException(e.getMessage());
-                }
-                return insertImage(cr, source, name, description);
-            }
-
-            /**
-             * Insert an image and create a thumbnail for it.
-             *
-             * @param cr The content resolver to use
-             * @param source The stream to use for the image
-             * @param title The name of the image
-             * @param description The description of the image
-             * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
-             *              for any reason.
-             * @deprecated inserting of images should be performed using
-             *             {@link MediaColumns#IS_PENDING}, which offers richer
-             *             control over lifecycle.
-             */
-            @Deprecated
-            public static final String insertImage(ContentResolver cr, Bitmap source, String title,
-                    String description) {
-                if (TextUtils.isEmpty(title)) title = "Image";
-
-                final long now = System.currentTimeMillis();
-                final ContentValues values = new ContentValues();
-                values.put(MediaColumns.DISPLAY_NAME, title);
-                values.put(MediaColumns.MIME_TYPE, "image/jpeg");
-                values.put(MediaColumns.DATE_ADDED, now / 1000);
-                values.put(MediaColumns.DATE_MODIFIED, now / 1000);
-                values.put(MediaColumns.DATE_EXPIRES, (now + DateUtils.DAY_IN_MILLIS) / 1000);
-                values.put(MediaColumns.IS_PENDING, 1);
-
-                final Uri uri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
-                try {
-                    try (OutputStream out = cr.openOutputStream(uri)) {
-                        source.compress(Bitmap.CompressFormat.JPEG, 90, out);
-                    }
-
-                    // Everything went well above, publish it!
-                    values.clear();
-                    values.put(MediaColumns.IS_PENDING, 0);
-                    values.putNull(MediaColumns.DATE_EXPIRES);
-                    cr.update(uri, values, null, null);
-                    return uri.toString();
-                } catch (Exception e) {
-                    Log.w(TAG, "Failed to insert image", e);
-                    cr.delete(uri, null, null);
-                    return null;
-                }
-            }
-
-            /**
-             * Get the content:// style URI for the image media table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the image media table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("images")
-                        .appendPath("media").build();
-            }
-
-            /**
-             * Get the content:// style URI for a single row in the images table
-             * on the given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @param id the image to get the URI for
-             * @return the URI to the images table on the given volume
-             */
-            public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
-                return ContentUris.withAppendedId(getContentUri(volumeName), id);
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type of of this directory of
-             * images.  Note that each entry in this directory will have a standard
-             * image MIME type as appropriate -- for example, image/jpeg.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
-        }
-
-        /**
-         * This class provides utility methods to obtain thumbnails for various
-         * {@link Images} items.
-         *
-         * @deprecated Callers should migrate to using
-         *             {@link ContentResolver#loadThumbnail}, since it offers
-         *             richer control over requested thumbnail sizes and
-         *             cancellation behavior.
-         */
-        @Deprecated
-        public static class Thumbnails implements BaseColumns {
-            /**
-             * @deprecated all queries should be performed through
-             *             {@link ContentResolver} directly, which offers modern
-             *             features like {@link CancellationSignal}.
-             */
-            @Deprecated
-            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
-                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
-            }
-
-            /**
-             * @deprecated all queries should be performed through
-             *             {@link ContentResolver} directly, which offers modern
-             *             features like {@link CancellationSignal}.
-             */
-            @Deprecated
-            public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
-                    String[] projection) {
-                return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
-            }
-
-            /**
-             * @deprecated all queries should be performed through
-             *             {@link ContentResolver} directly, which offers modern
-             *             features like {@link CancellationSignal}.
-             */
-            @Deprecated
-            public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
-                    String[] projection) {
-                return cr.query(EXTERNAL_CONTENT_URI, projection,
-                        IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
-                        kind, null, null);
-            }
-
-            /**
-             * Cancel any outstanding {@link #getThumbnail} requests, causing
-             * them to return by throwing a {@link OperationCanceledException}.
-             * <p>
-             * This method has no effect on
-             * {@link ContentResolver#loadThumbnail} calls, since they provide
-             * their own {@link CancellationSignal}.
-             *
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
-                final Uri uri = ContentUris.withAppendedId(
-                        Images.Media.EXTERNAL_CONTENT_URI, origId);
-                InternalThumbnails.cancelThumbnail(cr, uri);
-            }
-
-            /**
-             * Return thumbnail representing a specific image item. If a
-             * thumbnail doesn't exist, this method will block until it's
-             * generated. Callers are responsible for their own in-memory
-             * caching of returned values.
-             *
-             * As of {@link android.os.Build.VERSION_CODES#Q}, this output
-             * of the thumbnail has correct rotation, don't need to rotate
-             * it again.
-             *
-             * @param imageId the image item to obtain a thumbnail for.
-             * @param kind optimal thumbnail size desired.
-             * @return decoded thumbnail, or {@code null} if problem was
-             *         encountered.
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static Bitmap getThumbnail(ContentResolver cr, long imageId, int kind,
-                    BitmapFactory.Options options) {
-                final Uri uri = ContentUris.withAppendedId(
-                        Images.Media.EXTERNAL_CONTENT_URI, imageId);
-                return InternalThumbnails.getThumbnail(cr, uri, kind, options);
-            }
-
-            /**
-             * Cancel any outstanding {@link #getThumbnail} requests, causing
-             * them to return by throwing a {@link OperationCanceledException}.
-             * <p>
-             * This method has no effect on
-             * {@link ContentResolver#loadThumbnail} calls, since they provide
-             * their own {@link CancellationSignal}.
-             *
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId,
-                    long groupId) {
-                cancelThumbnailRequest(cr, origId);
-            }
-
-            /**
-             * Return thumbnail representing a specific image item. If a
-             * thumbnail doesn't exist, this method will block until it's
-             * generated. Callers are responsible for their own in-memory
-             * caching of returned values.
-             *
-             * As of {@link android.os.Build.VERSION_CODES#Q}, this output
-             * of the thumbnail has correct rotation, don't need to rotate
-             * it again.
-             *
-             * @param imageId the image item to obtain a thumbnail for.
-             * @param kind optimal thumbnail size desired.
-             * @return decoded thumbnail, or {@code null} if problem was
-             *         encountered.
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static Bitmap getThumbnail(ContentResolver cr, long imageId, long groupId,
-                    int kind, BitmapFactory.Options options) {
-                return getThumbnail(cr, imageId, kind, options);
-            }
-
-            /**
-             * Get the content:// style URI for the image media table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the image media table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("images")
-                        .appendPath("thumbnails").build();
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = "image_id ASC";
-
-            /**
-             * Path to the thumbnail file on disk.
-             * <p>
-             * Note that apps may not have filesystem permissions to directly
-             * access this path. Instead of trying to open this path directly,
-             * apps should use
-             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
-             * access.
-             *
-             * As of {@link android.os.Build.VERSION_CODES#Q}, this thumbnail
-             * has correct rotation, don't need to rotate it again.
-             *
-             * @deprecated Apps may not have filesystem permissions to directly
-             *             access this path. Instead of trying to open this path
-             *             directly, apps should use
-             *             {@link ContentResolver#loadThumbnail}
-             *             to gain access.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String DATA = "_data";
-
-            /**
-             * The original image for the thumbnal
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String IMAGE_ID = "image_id";
-
-            /**
-             * The kind of the thumbnail
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String KIND = "kind";
-
-            public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
-            public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
-            public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
-
-            /**
-             * Return the typical {@link Size} (in pixels) used internally when
-             * the given thumbnail kind is requested.
-             */
-            public static @NonNull Size getKindSize(int kind) {
-                return ThumbnailConstants.getKindSize(kind);
-            }
-
-            /**
-             * The blob raw data of thumbnail
-             *
-             * @deprecated this column never existed internally, and could never
-             *             have returned valid data.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_BLOB)
-            public static final String THUMB_DATA = "thumb_data";
-
-            /**
-             * The width of the thumbnal
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String WIDTH = "width";
-
-            /**
-             * The height of the thumbnail
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String HEIGHT = "height";
-        }
-    }
-
-    /**
-     * Collection of all media with MIME type of {@code audio/*}.
-     */
-    public static final class Audio {
-        /**
-         * Audio metadata columns.
-         */
-        public interface AudioColumns extends MediaColumns {
-
-            /**
-             * A non human readable key calculated from the TITLE, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String TITLE_KEY = "title_key";
-
-            /** @removed promoted to parent interface */
-            public static final String DURATION = "duration";
-
-            /**
-             * The position within the audio item at which playback should be
-             * resumed.
-             */
-            @DurationMillisLong
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String BOOKMARK = "bookmark";
-
-            /**
-             * The id of the artist who created the audio file, if any
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String ARTIST_ID = "artist_id";
-
-            /** @removed promoted to parent interface */
-            public static final String ARTIST = "artist";
-
-            /**
-             * The artist credited for the album that contains the audio file
-             * @hide
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ALBUM_ARTIST = "album_artist";
-
-            /**
-             * A non human readable key calculated from the ARTIST, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ARTIST_KEY = "artist_key";
-
-            /** @removed promoted to parent interface */
-            public static final String COMPOSER = "composer";
-
-            /**
-             * The id of the album the audio file is from, if any
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String ALBUM_ID = "album_id";
-
-            /** @removed promoted to parent interface */
-            public static final String ALBUM = "album";
-
-            /**
-             * A non human readable key calculated from the ALBUM, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ALBUM_KEY = "album_key";
-
-            /**
-             * The track number of this song on the album, if any.
-             * This number encodes both the track number and the
-             * disc number. For multi-disc sets, this number will
-             * be 1xxx for tracks on the first disc, 2xxx for tracks
-             * on the second disc, etc.
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String TRACK = "track";
-
-            /**
-             * The year the audio file was recorded, if any
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String YEAR = "year";
-
-            /**
-             * Non-zero if the audio file is music
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_MUSIC = "is_music";
-
-            /**
-             * Non-zero if the audio file is a podcast
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_PODCAST = "is_podcast";
-
-            /**
-             * Non-zero if the audio file may be a ringtone
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_RINGTONE = "is_ringtone";
-
-            /**
-             * Non-zero if the audio file may be an alarm
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_ALARM = "is_alarm";
-
-            /**
-             * Non-zero if the audio file may be a notification sound
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_NOTIFICATION = "is_notification";
-
-            /**
-             * Non-zero if the audio file is an audiobook
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String IS_AUDIOBOOK = "is_audiobook";
-
-            /**
-             * The id of the genre the audio file is from, if any
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String GENRE_ID = "genre_id";
-
-            /**
-             * The genre of the audio file, if any.
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String GENRE = "genre";
-
-            /**
-             * A non human readable key calculated from the GENRE, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String GENRE_KEY = "genre_key";
-
-            /**
-             * The resource URI of a localized title, if any.
-             * <p>
-             * Conforms to this pattern:
-             * <ul>
-             * <li>Scheme: {@link ContentResolver#SCHEME_ANDROID_RESOURCE}
-             * <li>Authority: Package Name of ringtone title provider
-             * <li>First Path Segment: Type of resource (must be "string")
-             * <li>Second Path Segment: Resource ID of title
-             * </ul>
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String TITLE_RESOURCE_URI = "title_resource_uri";
-        }
-
-        private static final Pattern PATTERN_TRIM_BEFORE = Pattern.compile(
-                "(?i)(^(the|an|a) |,\\s*(the|an|a)$|[^\\w\\s]|^\\s+|\\s+$)");
-        private static final Pattern PATTERN_TRIM_AFTER = Pattern.compile(
-                "(^(00)+|(00)+$)");
-
-        /**
-         * Converts a user-visible string into a "key" that can be used for
-         * grouping, sorting, and searching.
-         *
-         * @return Opaque token that should not be parsed or displayed to users.
-         * @deprecated These keys are generated using
-         *             {@link java.util.Locale#ROOT}, which means they don't
-         *             reflect locale-specific sorting preferences. To apply
-         *             locale-specific sorting preferences, use
-         *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-         *             {@code COLLATE LOCALIZED}, or
-         *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-         */
-        @Deprecated
-        public static @Nullable String keyFor(@Nullable String name) {
-            if (TextUtils.isEmpty(name)) return null;
-
-            if (UNKNOWN_STRING.equals(name)) {
-                return "01";
-            }
-
-            final boolean sortFirst = name.startsWith("\001");
-
-            name = PATTERN_TRIM_BEFORE.matcher(name).replaceAll("");
-            if (TextUtils.isEmpty(name)) return null;
-
-            final Collator c = Collator.getInstance(Locale.ROOT);
-            c.setStrength(Collator.PRIMARY);
-            name = HexEncoding.encodeToString(c.getCollationKey(name).toByteArray(), false);
-
-            name = PATTERN_TRIM_AFTER.matcher(name).replaceAll("");
-            if (sortFirst) {
-                name = "01" + name;
-            }
-            return name;
-        }
-
-        public static final class Media implements AudioColumns {
-            /**
-             * Get the content:// style URI for the audio media table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the audio media table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
-                        .appendPath("media").build();
-            }
-
-            /**
-             * Get the content:// style URI for a single row in the audio table
-             * on the given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @param id the audio to get the URI for
-             * @return the URI to the audio table on the given volume
-             */
-            public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
-                return ContentUris.withAppendedId(getContentUri(volumeName), id);
-            }
-
-            /**
-             * Get the content:// style URI for the given audio media file.
-             *
-             * @deprecated Apps may not have filesystem permissions to directly
-             *             access this path.
-             */
-            @Deprecated
-            public static @Nullable Uri getContentUriForPath(@NonNull String path) {
-                return getContentUri(getVolumeName(new File(path)));
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type for this table.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
-
-            /**
-             * The MIME type for an audio track.
-             */
-            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/audio";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
-
-            /**
-             * Activity Action: Start SoundRecorder application.
-             * <p>Input: nothing.
-             * <p>Output: An uri to the recorded sound stored in the Media Library
-             * if the recording was successful.
-             * May also contain the extra EXTRA_MAX_BYTES.
-             * @see #EXTRA_MAX_BYTES
-             */
-            @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-            public static final String RECORD_SOUND_ACTION =
-                    "android.provider.MediaStore.RECORD_SOUND";
-
-            /**
-             * The name of the Intent-extra used to define a maximum file size for
-             * a recording made by the SoundRecorder application.
-             *
-             * @see #RECORD_SOUND_ACTION
-             */
-             public static final String EXTRA_MAX_BYTES =
-                    "android.provider.MediaStore.extra.MAX_BYTES";
-        }
-
-        /**
-         * Audio genre metadata columns.
-         */
-        public interface GenresColumns {
-            /**
-             * The name of the genre
-             */
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String NAME = "name";
-        }
-
-        /**
-         * Contains all genres for audio files
-         */
-        public static final class Genres implements BaseColumns, GenresColumns {
-            /**
-             * Get the content:// style URI for the audio genres table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the audio genres table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
-                        .appendPath("genres").build();
-            }
-
-            /**
-             * Get the content:// style URI for querying the genres of an audio file.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @param audioId the ID of the audio file for which to retrieve the genres
-             * @return the URI to for querying the genres for the audio file
-             * with the given the volume and audioID
-             */
-            public static Uri getContentUriForAudioId(String volumeName, int audioId) {
-                return ContentUris.withAppendedId(Audio.Media.getContentUri(volumeName), audioId)
-                        .buildUpon().appendPath("genres").build();
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type for this table.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
-
-            /**
-             * The MIME type for entries in this table.
-             */
-            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = NAME;
-
-            /**
-             * Sub-directory of each genre containing all members.
-             */
-            public static final class Members implements AudioColumns {
-
-                public static final Uri getContentUri(String volumeName, long genreId) {
-                    return ContentUris
-                            .withAppendedId(Audio.Genres.getContentUri(volumeName), genreId)
-                            .buildUpon().appendPath("members").build();
-                }
-
-                /**
-                 * A subdirectory of each genre containing all member audio files.
-                 */
-                public static final String CONTENT_DIRECTORY = "members";
-
-                /**
-                 * The default sort order for this table
-                 */
-                public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
-
-                /**
-                 * The ID of the audio file
-                 */
-                @Column(Cursor.FIELD_TYPE_INTEGER)
-                public static final String AUDIO_ID = "audio_id";
-
-                /**
-                 * The ID of the genre
-                 */
-                @Column(Cursor.FIELD_TYPE_INTEGER)
-                public static final String GENRE_ID = "genre_id";
-            }
-        }
-
-        /**
-         * Audio playlist metadata columns.
-         */
-        public interface PlaylistsColumns {
-            /**
-             * The name of the playlist
-             */
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String NAME = "name";
-
-            /**
-             * Path to the playlist file on disk.
-             * <p>
-             * Note that apps may not have filesystem permissions to directly
-             * access this path. Instead of trying to open this path directly,
-             * apps should use
-             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
-             * access.
-             *
-             * @deprecated Apps may not have filesystem permissions to directly
-             *             access this path. Instead of trying to open this path
-             *             directly, apps should use
-             *             {@link ContentResolver#openFileDescriptor(Uri, String)}
-             *             to gain access.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String DATA = "_data";
-
-            /**
-             * The time the media item was first added.
-             */
-            @CurrentTimeSecondsLong
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String DATE_ADDED = "date_added";
-
-            /**
-             * The time the media item was last modified.
-             */
-            @CurrentTimeSecondsLong
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String DATE_MODIFIED = "date_modified";
-        }
-
-        /**
-         * Contains playlists for audio files
-         */
-        public static final class Playlists implements BaseColumns,
-                PlaylistsColumns {
-            /**
-             * Get the content:// style URI for the audio playlists table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the audio playlists table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
-                        .appendPath("playlists").build();
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type for this table.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
-
-            /**
-             * The MIME type for entries in this table.
-             */
-            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = NAME;
-
-            /**
-             * Sub-directory of each playlist containing all members.
-             */
-            public static final class Members implements AudioColumns {
-                public static final Uri getContentUri(String volumeName, long playlistId) {
-                    return ContentUris
-                            .withAppendedId(Audio.Playlists.getContentUri(volumeName), playlistId)
-                            .buildUpon().appendPath("members").build();
-                }
-
-                /**
-                 * Convenience method to move a playlist item to a new location
-                 * @param res The content resolver to use
-                 * @param playlistId The numeric id of the playlist
-                 * @param from The position of the item to move
-                 * @param to The position to move the item to
-                 * @return true on success
-                 */
-                public static final boolean moveItem(ContentResolver res,
-                        long playlistId, int from, int to) {
-                    Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
-                            playlistId)
-                            .buildUpon()
-                            .appendEncodedPath(String.valueOf(from))
-                            .appendQueryParameter("move", "true")
-                            .build();
-                    ContentValues values = new ContentValues();
-                    values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
-                    return res.update(uri, values, null, null) != 0;
-                }
-
-                /**
-                 * The ID within the playlist.
-                 */
-                @Column(Cursor.FIELD_TYPE_INTEGER)
-                public static final String _ID = "_id";
-
-                /**
-                 * A subdirectory of each playlist containing all member audio
-                 * files.
-                 */
-                public static final String CONTENT_DIRECTORY = "members";
-
-                /**
-                 * The ID of the audio file
-                 */
-                @Column(Cursor.FIELD_TYPE_INTEGER)
-                public static final String AUDIO_ID = "audio_id";
-
-                /**
-                 * The ID of the playlist
-                 */
-                @Column(Cursor.FIELD_TYPE_INTEGER)
-                public static final String PLAYLIST_ID = "playlist_id";
-
-                /**
-                 * The order of the songs in the playlist
-                 */
-                @Column(Cursor.FIELD_TYPE_INTEGER)
-                public static final String PLAY_ORDER = "play_order";
-
-                /**
-                 * The default sort order for this table
-                 */
-                public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
-            }
-        }
-
-        /**
-         * Audio artist metadata columns.
-         */
-        public interface ArtistColumns {
-            /**
-             * The artist who created the audio file, if any
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ARTIST = "artist";
-
-            /**
-             * A non human readable key calculated from the ARTIST, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ARTIST_KEY = "artist_key";
-
-            /**
-             * The number of albums in the database for this artist
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String NUMBER_OF_ALBUMS = "number_of_albums";
-
-            /**
-             * The number of albums in the database for this artist
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String NUMBER_OF_TRACKS = "number_of_tracks";
-        }
-
-        /**
-         * Contains artists for audio files
-         */
-        public static final class Artists implements BaseColumns, ArtistColumns {
-            /**
-             * Get the content:// style URI for the artists table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the audio artists table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
-                        .appendPath("artists").build();
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type for this table.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
-
-            /**
-             * The MIME type for entries in this table.
-             */
-            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
-
-            /**
-             * Sub-directory of each artist containing all albums on which
-             * a song by the artist appears.
-             */
-            public static final class Albums implements AlbumColumns {
-                public static final Uri getContentUri(String volumeName,long artistId) {
-                    return ContentUris
-                            .withAppendedId(Audio.Artists.getContentUri(volumeName), artistId)
-                            .buildUpon().appendPath("albums").build();
-                }
-            }
-        }
-
-        /**
-         * Audio album metadata columns.
-         */
-        public interface AlbumColumns {
-
-            /**
-             * The id for the album
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String ALBUM_ID = "album_id";
-
-            /**
-             * The album on which the audio file appears, if any
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ALBUM = "album";
-
-            /**
-             * The ID of the artist whose songs appear on this album.
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String ARTIST_ID = "artist_id";
-
-            /**
-             * The name of the artist whose songs appear on this album.
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ARTIST = "artist";
-
-            /**
-             * A non human readable key calculated from the ARTIST, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ARTIST_KEY = "artist_key";
-
-            /**
-             * The number of songs on this album
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String NUMBER_OF_SONGS = "numsongs";
-
-            /**
-             * This column is available when getting album info via artist,
-             * and indicates the number of songs on the album by the given
-             * artist.
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
-
-            /**
-             * The year in which the earliest songs
-             * on this album were released. This will often
-             * be the same as {@link #LAST_YEAR}, but for compilation albums
-             * they might differ.
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String FIRST_YEAR = "minyear";
-
-            /**
-             * The year in which the latest songs
-             * on this album were released. This will often
-             * be the same as {@link #FIRST_YEAR}, but for compilation albums
-             * they might differ.
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String LAST_YEAR = "maxyear";
-
-            /**
-             * A non human readable key calculated from the ALBUM, used for
-             * searching, sorting and grouping
-             *
-             * @see Audio#keyFor(String)
-             * @deprecated These keys are generated using
-             *             {@link java.util.Locale#ROOT}, which means they don't
-             *             reflect locale-specific sorting preferences. To apply
-             *             locale-specific sorting preferences, use
-             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
-             *             {@code COLLATE LOCALIZED}, or
-             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String ALBUM_KEY = "album_key";
-
-            /**
-             * Cached album art.
-             *
-             * @deprecated Apps may not have filesystem permissions to directly
-             *             access this path. Instead of trying to open this path
-             *             directly, apps should use
-             *             {@link ContentResolver#loadThumbnail}
-             *             to gain access.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String ALBUM_ART = "album_art";
-        }
-
-        /**
-         * Contains artists for audio files
-         */
-        public static final class Albums implements BaseColumns, AlbumColumns {
-            /**
-             * Get the content:// style URI for the albums table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the audio albums table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
-                        .appendPath("albums").build();
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type for this table.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
-
-            /**
-             * The MIME type for entries in this table.
-             */
-            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
-        }
-
-        public static final class Radio {
-            /**
-             * The MIME type for entries in this table.
-             */
-            public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
-
-            // Not instantiable.
-            private Radio() { }
-        }
-
-        /**
-         * This class provides utility methods to obtain thumbnails for various
-         * {@link Audio} items.
-         *
-         * @deprecated Callers should migrate to using
-         *             {@link ContentResolver#loadThumbnail}, since it offers
-         *             richer control over requested thumbnail sizes and
-         *             cancellation behavior.
-         * @hide
-         */
-        @Deprecated
-        public static class Thumbnails implements BaseColumns {
-            /**
-             * Path to the thumbnail file on disk.
-             * <p>
-             * Note that apps may not have filesystem permissions to directly
-             * access this path. Instead of trying to open this path directly,
-             * apps should use
-             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
-             * access.
-             *
-             * @deprecated Apps may not have filesystem permissions to directly
-             *             access this path. Instead of trying to open this path
-             *             directly, apps should use
-             *             {@link ContentResolver#loadThumbnail}
-             *             to gain access.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String DATA = "_data";
-
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String ALBUM_ID = "album_id";
-        }
-    }
-
-    /**
-     * Collection of all media with MIME type of {@code video/*}.
-     */
-    public static final class Video {
-
-        /**
-         * The default sort order for this table.
-         */
-        public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
-
-        /**
-         * @deprecated all queries should be performed through
-         *             {@link ContentResolver} directly, which offers modern
-         *             features like {@link CancellationSignal}.
-         */
-        @Deprecated
-        public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
-            return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
-        }
-
-        /**
-         * Video metadata columns.
-         */
-        public interface VideoColumns extends MediaColumns {
-            /** @removed promoted to parent interface */
-            public static final String DURATION = "duration";
-            /** @removed promoted to parent interface */
-            public static final String ARTIST = "artist";
-            /** @removed promoted to parent interface */
-            public static final String ALBUM = "album";
-            /** @removed promoted to parent interface */
-            public static final String RESOLUTION = "resolution";
-
-            /**
-             * The description of the video recording
-             */
-            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
-            public static final String DESCRIPTION = "description";
-
-            /**
-             * Whether the video should be published as public or private
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String IS_PRIVATE = "isprivate";
-
-            /**
-             * The user-added tags associated with a video
-             */
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String TAGS = "tags";
-
-            /**
-             * The YouTube category of the video
-             */
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String CATEGORY = "category";
-
-            /**
-             * The language of the video
-             */
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String LANGUAGE = "language";
-
-            /**
-             * The latitude where the video was captured.
-             *
-             * @deprecated location details are no longer indexed for privacy
-             *             reasons, and this value is now always {@code null}.
-             *             You can still manually obtain location metadata using
-             *             {@link ExifInterface#getLatLong(float[])}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
-            public static final String LATITUDE = "latitude";
-
-            /**
-             * The longitude where the video was captured.
-             *
-             * @deprecated location details are no longer indexed for privacy
-             *             reasons, and this value is now always {@code null}.
-             *             You can still manually obtain location metadata using
-             *             {@link ExifInterface#getLatLong(float[])}.
-             */
-            @Deprecated
-            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
-            public static final String LONGITUDE = "longitude";
-
-            /** @removed promoted to parent interface */
-            public static final String DATE_TAKEN = "datetaken";
-
-            /**
-             * The mini thumb id.
-             *
-             * @deprecated all thumbnails should be obtained via
-             *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
-             *             value is no longer supported.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
-
-            /** @removed promoted to parent interface */
-            public static final String BUCKET_ID = "bucket_id";
-            /** @removed promoted to parent interface */
-            public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
-            /** @removed promoted to parent interface */
-            public static final String GROUP_ID = "group_id";
-
-            /**
-             * The position within the video item at which playback should be
-             * resumed.
-             */
-            @DurationMillisLong
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String BOOKMARK = "bookmark";
-
-            /**
-             * The color standard of this media file, if available.
-             *
-             * @see MediaFormat#COLOR_STANDARD_BT709
-             * @see MediaFormat#COLOR_STANDARD_BT601_PAL
-             * @see MediaFormat#COLOR_STANDARD_BT601_NTSC
-             * @see MediaFormat#COLOR_STANDARD_BT2020
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String COLOR_STANDARD = "color_standard";
-
-            /**
-             * The color transfer of this media file, if available.
-             *
-             * @see MediaFormat#COLOR_TRANSFER_LINEAR
-             * @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
-             * @see MediaFormat#COLOR_TRANSFER_ST2084
-             * @see MediaFormat#COLOR_TRANSFER_HLG
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String COLOR_TRANSFER = "color_transfer";
-
-            /**
-             * The color range of this media file, if available.
-             *
-             * @see MediaFormat#COLOR_RANGE_LIMITED
-             * @see MediaFormat#COLOR_RANGE_FULL
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String COLOR_RANGE = "color_range";
-        }
-
-        public static final class Media implements VideoColumns {
-            /**
-             * Get the content:// style URI for the video media table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the video media table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("video")
-                        .appendPath("media").build();
-            }
-
-            /**
-             * Get the content:// style URI for a single row in the videos table
-             * on the given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @param id the video to get the URI for
-             * @return the URI to the videos table on the given volume
-             */
-            public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
-                return ContentUris.withAppendedId(getContentUri(volumeName), id);
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The MIME type for this table.
-             */
-            public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = TITLE;
-        }
-
-        /**
-         * This class provides utility methods to obtain thumbnails for various
-         * {@link Video} items.
-         *
-         * @deprecated Callers should migrate to using
-         *             {@link ContentResolver#loadThumbnail}, since it offers
-         *             richer control over requested thumbnail sizes and
-         *             cancellation behavior.
-         */
-        @Deprecated
-        public static class Thumbnails implements BaseColumns {
-            /**
-             * Cancel any outstanding {@link #getThumbnail} requests, causing
-             * them to return by throwing a {@link OperationCanceledException}.
-             * <p>
-             * This method has no effect on
-             * {@link ContentResolver#loadThumbnail} calls, since they provide
-             * their own {@link CancellationSignal}.
-             *
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
-                final Uri uri = ContentUris.withAppendedId(
-                        Video.Media.EXTERNAL_CONTENT_URI, origId);
-                InternalThumbnails.cancelThumbnail(cr, uri);
-            }
-
-            /**
-             * Return thumbnail representing a specific video item. If a
-             * thumbnail doesn't exist, this method will block until it's
-             * generated. Callers are responsible for their own in-memory
-             * caching of returned values.
-             *
-             * @param videoId the video item to obtain a thumbnail for.
-             * @param kind optimal thumbnail size desired.
-             * @return decoded thumbnail, or {@code null} if problem was
-             *         encountered.
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind,
-                    BitmapFactory.Options options) {
-                final Uri uri = ContentUris.withAppendedId(
-                        Video.Media.EXTERNAL_CONTENT_URI, videoId);
-                return InternalThumbnails.getThumbnail(cr, uri, kind, options);
-            }
-
-            /**
-             * Cancel any outstanding {@link #getThumbnail} requests, causing
-             * them to return by throwing a {@link OperationCanceledException}.
-             * <p>
-             * This method has no effect on
-             * {@link ContentResolver#loadThumbnail} calls, since they provide
-             * their own {@link CancellationSignal}.
-             *
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static void cancelThumbnailRequest(ContentResolver cr, long videoId,
-                    long groupId) {
-                cancelThumbnailRequest(cr, videoId);
-            }
-
-            /**
-             * Return thumbnail representing a specific video item. If a
-             * thumbnail doesn't exist, this method will block until it's
-             * generated. Callers are responsible for their own in-memory
-             * caching of returned values.
-             *
-             * @param videoId the video item to obtain a thumbnail for.
-             * @param kind optimal thumbnail size desired.
-             * @return decoded thumbnail, or {@code null} if problem was
-             *         encountered.
-             * @deprecated Callers should migrate to using
-             *             {@link ContentResolver#loadThumbnail}, since it
-             *             offers richer control over requested thumbnail sizes
-             *             and cancellation behavior.
-             */
-            @Deprecated
-            public static Bitmap getThumbnail(ContentResolver cr, long videoId, long groupId,
-                    int kind, BitmapFactory.Options options) {
-                return getThumbnail(cr, videoId, kind, options);
-            }
-
-            /**
-             * Get the content:// style URI for the image media table on the
-             * given volume.
-             *
-             * @param volumeName the name of the volume to get the URI for
-             * @return the URI to the image media table on the given volume
-             */
-            public static Uri getContentUri(String volumeName) {
-                return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("video")
-                        .appendPath("thumbnails").build();
-            }
-
-            /**
-             * The content:// style URI for the internal storage.
-             */
-            public static final Uri INTERNAL_CONTENT_URI =
-                    getContentUri("internal");
-
-            /**
-             * The content:// style URI for the "primary" external storage
-             * volume.
-             */
-            public static final Uri EXTERNAL_CONTENT_URI =
-                    getContentUri("external");
-
-            /**
-             * The default sort order for this table
-             */
-            public static final String DEFAULT_SORT_ORDER = "video_id ASC";
-
-            /**
-             * Path to the thumbnail file on disk.
-             *
-             * @deprecated Apps may not have filesystem permissions to directly
-             *             access this path. Instead of trying to open this path
-             *             directly, apps should use
-             *             {@link ContentResolver#openFileDescriptor(Uri, String)}
-             *             to gain access.
-             */
-            @Deprecated
-            @Column(Cursor.FIELD_TYPE_STRING)
-            public static final String DATA = "_data";
-
-            /**
-             * The original image for the thumbnal
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String VIDEO_ID = "video_id";
-
-            /**
-             * The kind of the thumbnail
-             */
-            @Column(Cursor.FIELD_TYPE_INTEGER)
-            public static final String KIND = "kind";
-
-            public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
-            public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
-            public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
-
-            /**
-             * Return the typical {@link Size} (in pixels) used internally when
-             * the given thumbnail kind is requested.
-             */
-            public static @NonNull Size getKindSize(int kind) {
-                return ThumbnailConstants.getKindSize(kind);
-            }
-
-            /**
-             * The width of the thumbnal
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String WIDTH = "width";
-
-            /**
-             * The height of the thumbnail
-             */
-            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
-            public static final String HEIGHT = "height";
-        }
-    }
-
-    /** @removed */
-    @Deprecated
-    public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) {
-        return getExternalVolumeNames(context);
-    }
-
-    /**
-     * Return list of all specific volume names that make up
-     * {@link #VOLUME_EXTERNAL}. This includes a unique volume name for each
-     * shared storage device that is currently attached, which typically
-     * includes {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}.
-     * <p>
-     * Each specific volume name can be passed to APIs like
-     * {@link MediaStore.Images.Media#getContentUri(String)} to interact with
-     * media on that storage device.
-     */
-    public static @NonNull Set<String> getExternalVolumeNames(@NonNull Context context) {
-        final StorageManager sm = context.getSystemService(StorageManager.class);
-        final Set<String> res = new ArraySet<>();
-        for (StorageVolume sv : sm.getStorageVolumes()) {
-            switch (sv.getState()) {
-                case Environment.MEDIA_MOUNTED:
-                case Environment.MEDIA_MOUNTED_READ_ONLY: {
-                    final String volumeName = sv.getMediaStoreVolumeName();
-                    if (volumeName != null) {
-                        res.add(volumeName);
-                    }
-                    break;
-                }
-            }
-        }
-        return res;
-    }
-
-    /**
-     * Return list of all recent volume names that have been part of
-     * {@link #VOLUME_EXTERNAL}.
-     * <p>
-     * These volume names are not currently mounted, but they're likely to
-     * reappear in the future, so apps are encouraged to preserve any indexed
-     * metadata related to these volumes to optimize user experiences.
-     * <p>
-     * Each specific volume name can be passed to APIs like
-     * {@link MediaStore.Images.Media#getContentUri(String)} to interact with
-     * media on that storage device.
-     */
-    public static @NonNull Set<String> getRecentExternalVolumeNames(@NonNull Context context) {
-        final StorageManager sm = context.getSystemService(StorageManager.class);
-        final Set<String> res = new ArraySet<>();
-        for (StorageVolume sv : sm.getRecentStorageVolumes()) {
-            final String volumeName = sv.getMediaStoreVolumeName();
-            if (volumeName != null) {
-                res.add(volumeName);
-            }
-        }
-        return res;
-    }
-
-    /**
-     * Return the volume name that the given {@link Uri} references.
-     */
-    public static @NonNull String getVolumeName(@NonNull Uri uri) {
-        final List<String> segments = uri.getPathSegments();
-        switch (uri.getAuthority()) {
-            case AUTHORITY:
-            case AUTHORITY_LEGACY: {
-                if (segments != null && segments.size() > 0) {
-                    return segments.get(0);
-                }
-            }
-        }
-        throw new IllegalArgumentException("Missing volume name: " + uri);
-    }
-
-    /** {@hide} */
-    public static @NonNull String checkArgumentVolumeName(@NonNull String volumeName) {
-        if (TextUtils.isEmpty(volumeName)) {
-            throw new IllegalArgumentException();
-        }
-
-        if (VOLUME_INTERNAL.equals(volumeName)) {
-            return volumeName;
-        } else if (VOLUME_EXTERNAL.equals(volumeName)) {
-            return volumeName;
-        } else if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName)) {
-            return volumeName;
-        }
-
-        // When not one of the well-known values above, it must be a hex UUID
-        for (int i = 0; i < volumeName.length(); i++) {
-            final char c = volumeName.charAt(i);
-            if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9') || (c == '-')) {
-                continue;
-            } else {
-                throw new IllegalArgumentException("Invalid volume name: " + volumeName);
-            }
-        }
-        return volumeName;
-    }
-
-    private static boolean parseBoolean(@Nullable String value) {
-        if (value == null) return false;
-        if ("1".equals(value)) return true;
-        if ("true".equalsIgnoreCase(value)) return true;
-        return false;
-    }
-
-    /**
-     * Uri for querying the state of the media scanner.
-     */
-    public static Uri getMediaScannerUri() {
-        return AUTHORITY_URI.buildUpon().appendPath("none").appendPath("media_scanner").build();
-    }
-
-    /**
-     * Name of current volume being scanned by the media scanner.
-     */
-    public static final String MEDIA_SCANNER_VOLUME = "volume";
-
-    /**
-     * Name of the file signaling the media scanner to ignore media in the containing directory
-     * and its subdirectories. Developers should use this to avoid application graphics showing
-     * up in the Gallery and likewise prevent application sounds and music from showing up in
-     * the Music app.
-     */
-    public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
-
-    /**
-     * Return an opaque version string describing the {@link MediaStore} state.
-     * <p>
-     * Applications that import data from {@link MediaStore} into their own
-     * caches can use this to detect that {@link MediaStore} has undergone
-     * substantial changes, and that data should be rescanned.
-     * <p>
-     * No other assumptions should be made about the meaning of the version.
-     * <p>
-     * This method returns the version for
-     * {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}; to obtain a version for a
-     * different volume, use {@link #getVersion(Context, String)}.
-     */
-    public static @NonNull String getVersion(@NonNull Context context) {
-        return getVersion(context, VOLUME_EXTERNAL_PRIMARY);
-    }
-
-    /**
-     * Return an opaque version string describing the {@link MediaStore} state.
-     * <p>
-     * Applications that import data from {@link MediaStore} into their own
-     * caches can use this to detect that {@link MediaStore} has undergone
-     * substantial changes, and that data should be rescanned.
-     * <p>
-     * No other assumptions should be made about the meaning of the version.
-     *
-     * @param volumeName specific volume to obtain an opaque version string for.
-     *            Must be one of the values returned from
-     *            {@link #getExternalVolumeNames(Context)}.
-     */
-    public static @NonNull String getVersion(@NonNull Context context, @NonNull String volumeName) {
-        final ContentResolver resolver = context.getContentResolver();
-        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
-            final Bundle in = new Bundle();
-            in.putString(Intent.EXTRA_TEXT, volumeName);
-            final Bundle out = client.call(GET_VERSION_CALL, null, in);
-            return out.getString(Intent.EXTRA_TEXT);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Return a {@link DocumentsProvider} Uri that is an equivalent to the given
-     * {@link MediaStore} Uri.
-     * <p>
-     * This allows apps with Storage Access Framework permissions to convert
-     * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
-     * to the same underlying item. Note that this method doesn't grant any new
-     * permissions; callers must already hold permissions obtained with
-     * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
-     *
-     * @param mediaUri The {@link MediaStore} Uri to convert.
-     * @return An equivalent {@link DocumentsProvider} Uri. Returns {@code null}
-     *         if no equivalent was found.
-     * @see #getMediaUri(Context, Uri)
-     */
-    public static @Nullable Uri getDocumentUri(@NonNull Context context, @NonNull Uri mediaUri) {
-        final ContentResolver resolver = context.getContentResolver();
-        final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
-
-        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
-            final Bundle in = new Bundle();
-            in.putParcelable(EXTRA_URI, mediaUri);
-            in.putParcelableArrayList(EXTRA_URI_PERMISSIONS, new ArrayList<>(uriPermissions));
-            final Bundle out = client.call(GET_DOCUMENT_URI_CALL, null, in);
-            return out.getParcelable(EXTRA_URI);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Return a {@link MediaStore} Uri that is an equivalent to the given
-     * {@link DocumentsProvider} Uri.
-     * <p>
-     * This allows apps with Storage Access Framework permissions to convert
-     * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
-     * to the same underlying item. Note that this method doesn't grant any new
-     * permissions; callers must already hold permissions obtained with
-     * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
-     *
-     * @param documentUri The {@link DocumentsProvider} Uri to convert.
-     * @return An equivalent {@link MediaStore} Uri. Returns {@code null} if no
-     *         equivalent was found.
-     * @see #getDocumentUri(Context, Uri)
-     */
-    public static @Nullable Uri getMediaUri(@NonNull Context context, @NonNull Uri documentUri) {
-        final ContentResolver resolver = context.getContentResolver();
-        final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
-
-        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
-            final Bundle in = new Bundle();
-            in.putParcelable(EXTRA_URI, documentUri);
-            in.putParcelableArrayList(EXTRA_URI_PERMISSIONS, new ArrayList<>(uriPermissions));
-            final Bundle out = client.call(GET_MEDIA_URI_CALL, null, in);
-            return out.getParcelable(EXTRA_URI);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
-    /** @hide */
-    @TestApi
-    public static void waitForIdle(@NonNull ContentResolver resolver) {
-        resolver.call(AUTHORITY, WAIT_FOR_IDLE_CALL, null, null);
-    }
-
-    /**
-     * Perform a blocking scan of the given {@link File}, returning the
-     * {@link Uri} of the scanned file.
-     *
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    @SuppressLint("StreamFiles")
-    public static @NonNull Uri scanFile(@NonNull ContentResolver resolver, @NonNull File file) {
-        final Bundle out = resolver.call(AUTHORITY, SCAN_FILE_CALL, file.getAbsolutePath(), null);
-        return out.getParcelable(Intent.EXTRA_STREAM);
-    }
-
-    /**
-     * Perform a blocking scan of the given storage volume.
-     *
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    public static void scanVolume(@NonNull ContentResolver resolver, @NonNull String volumeName) {
-        resolver.call(AUTHORITY, SCAN_VOLUME_CALL, volumeName, null);
-    }
-}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 503d6db..7629e1b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3900,6 +3900,12 @@
         public static final String VOLUME_ACCESSIBILITY = "volume_a11y";
 
         /**
+         * @hide
+         * Volume index for virtual assistant.
+         */
+        public static final String VOLUME_ASSISTANT = "volume_assistant";
+
+        /**
          * Master volume (float in the range 0.0f to 1.0f).
          *
          * @hide
@@ -3977,7 +3983,7 @@
                 "" /*STREAM_SYSTEM_ENFORCED, no setting for this stream*/,
                 "" /*STREAM_DTMF, no setting for this stream*/,
                 "" /*STREAM_TTS, no setting for this stream*/,
-                VOLUME_ACCESSIBILITY
+                VOLUME_ACCESSIBILITY, VOLUME_ASSISTANT
             };
 
         /**
@@ -4524,6 +4530,7 @@
             PUBLIC_SETTINGS.add(VOLUME_ALARM);
             PUBLIC_SETTINGS.add(VOLUME_NOTIFICATION);
             PUBLIC_SETTINGS.add(VOLUME_BLUETOOTH_SCO);
+            PUBLIC_SETTINGS.add(VOLUME_ASSISTANT);
             PUBLIC_SETTINGS.add(RINGTONE);
             PUBLIC_SETTINGS.add(NOTIFICATION_SOUND);
             PUBLIC_SETTINGS.add(ALARM_ALERT);
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index c287ae3..fed3d7ba 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1389,7 +1389,6 @@
                 for (int i = 0; i < pduCount; i++) {
                     byte[] pdu = (byte[]) messages[i];
                     msgs[i] = SmsMessage.createFromPdu(pdu, format);
-                    if (msgs[i] != null) msgs[i].setSubId(subId);
                 }
                 return msgs;
             }
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 373e1e5..54a4fa6 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.content.pm.DataLoaderParams;
@@ -45,30 +46,41 @@
  * The base class for implementing data loader service to control data loaders. Expecting
  * Incremental Service to bind to a children class of this.
  *
- * @hide
+ * WARNING: This is a system API to aid internal development.
+ * Use at your own risk. It will change or be removed without warning.
  *
- * Hide for now, should be @SystemApi
  * TODO(b/136132412): update with latest API design
+ *
+ * @hide
  */
+@SystemApi
 public abstract class DataLoaderService extends Service {
     private static final String TAG = "IncrementalDataLoaderService";
     private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
 
+    /** @hide */
     public static final int DATA_LOADER_READY =
             IDataLoaderStatusListener.DATA_LOADER_READY;
+    /** @hide */
     public static final int DATA_LOADER_NOT_READY =
             IDataLoaderStatusListener.DATA_LOADER_NOT_READY;
+    /** @hide */
     public static final int DATA_LOADER_RUNNING =
             IDataLoaderStatusListener.DATA_LOADER_RUNNING;
+    /** @hide */
     public static final int DATA_LOADER_STOPPED =
             IDataLoaderStatusListener.DATA_LOADER_STOPPED;
+    /** @hide */
     public static final int DATA_LOADER_SLOW_CONNECTION =
             IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION;
+    /** @hide */
     public static final int DATA_LOADER_NO_CONNECTION =
             IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION;
+    /** @hide */
     public static final int DATA_LOADER_CONNECTION_OK =
             IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK;
 
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"DATA_LOADER_"}, value = {
             DATA_LOADER_READY,
@@ -85,6 +97,7 @@
     /**
      * Managed DataLoader interface. Each instance corresponds to a single Incremental File System
      * instance.
+     * @hide
      */
     public abstract static class DataLoader {
         /**
@@ -130,6 +143,7 @@
      * DataLoader factory method.
      *
      * @return An instance of a DataLoader.
+     * @hide
      */
     public abstract @Nullable DataLoader onCreateDataLoader();
 
@@ -220,8 +234,6 @@
      * Used by the DataLoaderService implementations.
      *
      * @hide
-     *
-     * TODO(b/136132412) Should be @SystemApi
      */
     public static final class FileSystemConnector {
         /**
@@ -264,7 +276,6 @@
     /**
      * Wrapper for native reporting DataLoader statuses.
      * @hide
-     * TODO(b/136132412) Should be @SystemApi
      */
     public static final class StatusListener {
         /**
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 99bf93e..46025aa 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -32,14 +32,13 @@
     // and not be reordered
     int checkOperation(int code, int uid, String packageName);
     int noteOperation(int code, int uid, String packageName, @nullable String featureId);
-    int startOperation(IBinder token, int code, int uid, String packageName,
+    int startOperation(IBinder clientId, int code, int uid, String packageName,
             @nullable String featureId, boolean startIfModeDefault);
     @UnsupportedAppUsage
-    void finishOperation(IBinder token, int code, int uid, String packageName,
+    void finishOperation(IBinder clientId, int code, int uid, String packageName,
             @nullable String featureId);
     void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
     void stopWatchingMode(IAppOpsCallback callback);
-    IBinder getToken(IBinder clientToken);
     int permissionToOpCode(String permission);
     int checkAudioOperation(int code, int usage, int uid, String packageName);
     void noteAsyncOp(@nullable String callingPackageName, int uid, @nullable String packageName,
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index bc80197..c7ec2cd 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -136,6 +136,13 @@
     }
 
     /**
+     * Returns the same array or an empty one if it's null.
+     */
+    public static @NonNull <T> T[] emptyIfNull(@Nullable T[] items, Class<T> kind) {
+        return items != null ? items : emptyArray(kind);
+    }
+
+    /**
      * Checks if given array is null or has zero elements.
      */
     public static boolean isEmpty(@Nullable Collection<?> array) {
@@ -751,6 +758,42 @@
         return result;
     }
 
+    /**
+     * Returns an array containing elements from the given one that match the given predicate.
+     */
+    public static @Nullable <T> T[] filter(@Nullable T[] items,
+            @NonNull IntFunction<T[]> arrayConstructor,
+            @NonNull java.util.function.Predicate<T> predicate) {
+        if (isEmpty(items)) {
+            return items;
+        }
+
+        int matchesCount = 0;
+        int size = size(items);
+        for (int i = 0; i < size; i++) {
+            if (predicate.test(items[i])) {
+                matchesCount++;
+            }
+        }
+        if (matchesCount == 0) {
+            return items;
+        }
+        if (matchesCount == items.length) {
+            return items;
+        }
+        if (matchesCount == 0) {
+            return null;
+        }
+        T[] result = arrayConstructor.apply(matchesCount);
+        int outIdx = 0;
+        for (int i = 0; i < size; i++) {
+            if (predicate.test(items[i])) {
+                result[outIdx++] = items[i];
+            }
+        }
+        return result;
+    }
+
     public static boolean startsWith(byte[] cur, byte[] val) {
         if (cur == null || val == null) return false;
         if (cur.length < val.length) return false;
diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java
index c1d129b..362bc92 100644
--- a/core/java/com/android/internal/util/MemInfoReader.java
+++ b/core/java/com/android/internal/util/MemInfoReader.java
@@ -91,7 +91,15 @@
      * that are mapped in to processes.
      */
     public long getCachedSizeKb() {
-        return mInfos[Debug.MEMINFO_BUFFERS] + mInfos[Debug.MEMINFO_SLAB_RECLAIMABLE]
+        long kReclaimable = mInfos[Debug.MEMINFO_KRECLAIMABLE];
+
+        // Note: MEMINFO_KRECLAIMABLE includes MEMINFO_SLAB_RECLAIMABLE and ION pools.
+        // Fall back to using MEMINFO_SLAB_RECLAIMABLE in case of older kernels that do
+        // not include KReclaimable meminfo field.
+        if (kReclaimable == 0) {
+            kReclaimable = mInfos[Debug.MEMINFO_SLAB_RECLAIMABLE];
+        }
+        return mInfos[Debug.MEMINFO_BUFFERS] + kReclaimable
                 + mInfos[Debug.MEMINFO_CACHED] - mInfos[Debug.MEMINFO_MAPPED];
     }
 
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index a46ad6d..738965e 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -37,6 +37,7 @@
   "/apex/com.android.conscrypt/javalib/conscrypt.jar",
   "/apex/com.android.ipsec/javalib/ike.jar",
   "/apex/com.android.media/javalib/updatable-media.jar",
+  "/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
   "/apex/com.android.os.statsd/javalib/framework-statsd.jar",
   "/apex/com.android.sdkext/javalib/framework-sdkext.jar",
   "/apex/com.android.telephony/javalib/telephony-common.jar",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 039e458..b2a19cf 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -251,6 +251,9 @@
     // ACTION: Create a Settings shortcut item.
     ACTION_SETTINGS_CREATE_SHORTCUT = 829;
 
+    // ACTION: A tile in Settings information architecture is clicked
+    ACTION_SETTINGS_TILE_CLICK = 830;
+
     // ACTION: Settings advanced button is expanded
     ACTION_SETTINGS_ADVANCED_BUTTON_EXPAND = 834;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c32f521..1165d2d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -307,6 +307,7 @@
     <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
 
     <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
+    <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
     <protected-broadcast android:name="com.android.nfc.action.LLCP_UP" />
     <protected-broadcast android:name="com.android.nfc.action.LLCP_DOWN" />
@@ -1629,6 +1630,14 @@
     <permission android:name="android.permission.NETWORK_SETTINGS"
         android:protectionLevel="signature|telephony" />
 
+    <!-- Allows holder to request bluetooth/wifi scan bypassing global "use location" setting and
+         location permissions.
+         <p>Not for use by third-party or privileged applications.
+         @hide
+    -->
+    <permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"
+                android:protectionLevel="signature|companion" />
+
     <!-- Allows SetupWizard to call methods in Networking services
          <p>Not for use by any other third-party or privileged applications.
          @SystemApi
@@ -1749,6 +1758,14 @@
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.NFC_TRANSACTION_EVENT"
+      android:protectionLevel="normal" />
+
+    <!-- Allows applications to receive NFC preferred payment service information.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.NFC_PREFERRED_PAYMENT_INFO"
+        android:description="@string/permdesc_preferredPaymentInfo"
+        android:label="@string/permlab_preferredPaymentInfo"
         android:protectionLevel="normal" />
 
     <!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 166cde0..b17d473 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -298,6 +298,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system telephony apps -->
         <flag name="telephony" value="0x400000" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the system companion device manager service -->
+        <flag name="companion" value="0x800000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 85406c7..32ec95a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3572,6 +3572,13 @@
         <item>android.ext.services</item>
     </string-array>
 
+    <!-- The package name for the system companion device manager service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         Example: "com.android.companiondevicemanager"
+         See android.companion.CompanionDeviceManager.
+    -->
+    <string name="config_companionDeviceManagerPackage" translatable="false"></string>
+
     <!-- The package name for the default wellbeing app.
          This package must be trusted, as it has the permissions to control other applications
          on the device.
@@ -4272,4 +4279,7 @@
          value is left empty.  When this is non-empty then config_rawContactsLocalAccountName
          should also be non-empty.-->
     <string name="config_rawContactsLocalAccountType" translatable="false"></string>
+
+    <!-- Whether or not to use assistant stream volume separately from music volume -->
+    <bool name="config_useAssistantVolume">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ab10738..31becf7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1345,6 +1345,12 @@
       connections with paired devices.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_preferredPaymentInfo">Preferred NFC Payment Service Information</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_preferredPaymentInfo">Allows the app to get preferred nfc payment service information like
+      registered aids and route destination.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_nfc">control Near Field Communication</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_nfc">Allows the app to communicate
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1e59c37..d30c7bf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -468,6 +468,7 @@
   <java-symbol type="string" name="config_deviceSpecificAudioService" />
   <java-symbol type="integer" name="config_num_physical_slots" />
   <java-symbol type="array" name="config_integrityRuleProviderPackages" />
+  <java-symbol type="bool" name="config_useAssistantVolume" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 576ac73..5cb7852 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -239,4 +239,17 @@
         verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
                 .times(1)).onThermalStatusChanged(status);
     }
+
+    @Test
+    public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() {
+        // Can't use assumption framework with AndroidTestCase :(
+        if (mPm.isRebootingUserspaceSupported()) {
+            return;
+        }
+        try {
+            mPm.reboot(PowerManager.REBOOT_USERSPACE);
+            fail("UnsupportedOperationException not thrown");
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c8a7ef5..34ed5b3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -374,6 +374,10 @@
     public static final int STREAM_TTS = AudioSystem.STREAM_TTS;
     /** Used to identify the volume of audio streams for accessibility prompts */
     public static final int STREAM_ACCESSIBILITY = AudioSystem.STREAM_ACCESSIBILITY;
+    /** @hide Used to identify the volume of audio streams for virtual assistant */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public static final int STREAM_ASSISTANT = AudioSystem.STREAM_ASSISTANT;
 
     /** Number of audio streams */
     /**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8e76fcf..e584add 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -80,6 +80,8 @@
     public static final int STREAM_TTS = 9;
     /** Used to identify the volume of audio streams for accessibility prompts */
     public static final int STREAM_ACCESSIBILITY = 10;
+    /** Used to identify the volume of audio streams for virtual assistant */
+    public static final int STREAM_ASSISTANT = 11;
     /**
      * @deprecated Use {@link #numStreamTypes() instead}
      */
@@ -92,7 +94,7 @@
     private static native int native_get_FCC_8();
 
     // Expose only the getter method publicly so we can change it in the future
-    private static final int NUM_STREAM_TYPES = 11;
+    private static final int NUM_STREAM_TYPES = 12;
     @UnsupportedAppUsage
     public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; }
 
@@ -107,7 +109,8 @@
         "STREAM_SYSTEM_ENFORCED",
         "STREAM_DTMF",
         "STREAM_TTS",
-        "STREAM_ACCESSIBILITY"
+        "STREAM_ACCESSIBILITY",
+        "STREAM_ASSISTANT"
     };
 
     /*
@@ -1277,6 +1280,7 @@
         5, // STREAM_DTMF
         5, // STREAM_TTS
         5, // STREAM_ACCESSIBILITY
+        5, // STREAM_ASSISTANT
     };
 
     public static String streamToString(int stream) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 177f2b8..203adfc 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -40,6 +40,7 @@
     AConfiguration_getOrientation;
     AConfiguration_getScreenHeightDp; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     AConfiguration_getScreenLong;
+    AConfiguration_getScreenRound; # introduced=30
     AConfiguration_getScreenSize;
     AConfiguration_getScreenWidthDp; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     AConfiguration_getSdkVersion;
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index a379bfc..1453ec3 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -15,5 +15,6 @@
 android_app {
     name: "CompanionDeviceManager",
     srcs: ["src/**/*.java"],
+
     platform_apis: true,
 }
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 42885e8..a9c6685 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"/>
 
     <application
         android:allowClearUserData="true"
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 7b2922ba..1e19786 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -102,6 +102,9 @@
             DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
     private static final String ROOT_ID_HOME = "home";
 
+    private static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
+    private static final String GET_MEDIA_URI_CALL = "get_media_uri";
+
     private StorageManager mStorageManager;
     private UserManager mUserManager;
 
@@ -665,7 +668,7 @@
                     }
                     break;
                 }
-                case MediaStore.GET_DOCUMENT_URI_CALL: {
+                case GET_DOCUMENT_URI_CALL: {
                     // All callers must go through MediaProvider
                     getContext().enforceCallingPermission(
                             android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);
@@ -684,7 +687,7 @@
                         throw new IllegalStateException("File in " + path + " is not found.", e);
                     }
                 }
-                case MediaStore.GET_MEDIA_URI_CALL: {
+                case GET_MEDIA_URI_CALL: {
                     // All callers must go through MediaProvider
                     getContext().enforceCallingPermission(
                             android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
index e936c35..68a3729 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
@@ -17,8 +17,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.LongSparseLongArray;
-import android.util.Pair;
+import android.util.LongSparseArray;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -159,14 +158,11 @@
 
     private OpEntry createOpEntryWithTime(int op, long time) {
         // Slot for background access timestamp.
-        final LongSparseLongArray accessTimes = new LongSparseLongArray();
-        accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
-            AppOpsManager.OP_FLAG_SELF), time);
+        final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
+        accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
+            AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
 
-        OpFeatureEntry.Builder featureEntry = new OpFeatureEntry.Builder(false, accessTimes,
-                null /*rejectTimes*/, null /*durations*/, null /* proxyUids */,
-                null /* proxyPackages */, null /* proxyFeatureIds */);
-        return new OpEntry(op, AppOpsManager.MODE_ALLOWED,
-                new Pair[]{new Pair(null, featureEntry)});
+        return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
+                new OpFeatureEntry(op, false, accessEvents, null)));
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 3fde48b..3f8d758 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -17,6 +17,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
 import android.util.Pair;
 
@@ -158,17 +159,11 @@
     }
 
     private OpEntry createOpEntryWithTime(int op, long time, int duration) {
-        final LongSparseLongArray accessTimes = new LongSparseLongArray();
-        accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
-                AppOpsManager.OP_FLAG_SELF), time);
-        final LongSparseLongArray durations = new LongSparseLongArray();
-        durations.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
-                AppOpsManager.OP_FLAG_SELF), duration);
+        final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
+        accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
+                AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, duration, null));
 
-        OpFeatureEntry.Builder featureEntry = new OpFeatureEntry.Builder(false, accessTimes,
-                null /*rejectTimes*/, durations, null /* proxyUids */,
-                null /* proxyPackages */, null /* proxyFeatureIds */);
-        return new OpEntry(op, AppOpsManager.MODE_ALLOWED,
-                new Pair[]{new Pair(null, featureEntry)});
+        return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
+                new OpFeatureEntry(op, false, accessEvents, null)));
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 19ff244..443811f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -87,6 +87,7 @@
                     Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will
                                                           // not change volume
                     Settings.System.VOLUME_ALARM, // deprecated since API 2?
+                    Settings.System.VOLUME_ASSISTANT, // candidate for backup?
                     Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
                     Settings.System.VOLUME_MASTER, // candidate for backup?
                     Settings.System.VOLUME_MUSIC, // deprecated since API 2?
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index e3694ac..bfac4fc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.accessibility;
 
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
@@ -196,6 +198,7 @@
         params.token = mOverlayView.getWindowToken();
         params.x = mMagnificationFrame.left;
         params.y = mMagnificationFrame.top;
+        params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
         params.setTitle(mContext.getString(R.string.magnification_window_title));
 
         mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index d2f1113..78f278e 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -63,6 +63,9 @@
     public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
     public static final int PACKAGE_APP_PREDICTOR = 11;
     public static final int PACKAGE_TELEPHONY = 12;
+    public static final int PACKAGE_WIFI = 13;
+    public static final int PACKAGE_COMPANION = 14;
+
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -77,6 +80,8 @@
         PACKAGE_INCIDENT_REPORT_APPROVER,
         PACKAGE_APP_PREDICTOR,
         PACKAGE_TELEPHONY,
+        PACKAGE_WIFI,
+        PACKAGE_COMPANION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1852e01..6dc49b7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13051,14 +13051,31 @@
                     pw.println(totalPss - cachedPss);
                 }
             }
-            long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+            long kernelUsed = memInfo.getKernelUsedSizeKb();
+            final long ionHeap = Debug.getIonHeapsSizeKb();
+            if (ionHeap > 0) {
+                final long ionMapped = Debug.getIonMappedSizeKb();
+                final long ionUnmapped = ionHeap - ionMapped;
+                final long ionPool = Debug.getIonPoolsSizeKb();
+                pw.print("      ION: ");
+                        pw.print(stringifyKBSize(ionHeap + ionPool));
+                        pw.print(" (");
+                        pw.print(stringifyKBSize(ionMapped));
+                        pw.print(" mapped + ");
+                        pw.print(stringifyKBSize(ionUnmapped));
+                        pw.print(" unmapped + ");
+                        pw.print(stringifyKBSize(ionPool));
+                        pw.println(" pools)");
+                kernelUsed += ionUnmapped;
+            }
+            final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
-                    - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
+                    - kernelUsed - memInfo.getZramTotalSizeKb();
             if (!opts.isCompact) {
                 pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
-                        + memInfo.getKernelUsedSizeKb())); pw.print(" (");
+                        + kernelUsed)); pw.print(" (");
                 pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
-                pw.print(stringifyKBSize(memInfo.getKernelUsedSizeKb())); pw.print(" kernel)\n");
+                pw.print(stringifyKBSize(kernelUsed)); pw.print(" kernel)\n");
                 pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
             } else {
                 pw.print("lostram,"); pw.println(lostRAM);
@@ -13824,14 +13841,25 @@
         memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
                 + memInfo.getFreeSizeKb()));
         memInfoBuilder.append("\n");
+        long kernelUsed = memInfo.getKernelUsedSizeKb();
+        final long ionHeap = Debug.getIonHeapsSizeKb();
+        if (ionHeap > 0) {
+            final long ionMapped = Debug.getIonMappedSizeKb();
+            final long ionUnmapped = ionHeap - ionMapped;
+            final long ionPool = Debug.getIonPoolsSizeKb();
+            memInfoBuilder.append("       ION: ");
+            memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
+            memInfoBuilder.append("\n");
+            kernelUsed += ionUnmapped;
+        }
         memInfoBuilder.append("  Used RAM: ");
         memInfoBuilder.append(stringifyKBSize(
-                                  totalPss - cachedPss + memInfo.getKernelUsedSizeKb()));
+                                  totalPss - cachedPss + kernelUsed));
         memInfoBuilder.append("\n");
         memInfoBuilder.append("  Lost RAM: ");
         memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
                 - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
-                - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb()));
+                - kernelUsed - memInfo.getZramTotalSizeKb()));
         memInfoBuilder.append("\n");
         Slog.i(TAG, "Low on memory:");
         Slog.i(TAG, shortNativeBuilder.toString());
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 728291d..f9730a9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -407,6 +407,7 @@
      */
     private boolean finishUserUnlocking(final UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
+        Slog.d(TAG, "UserController event: finishUserUnlocking(" + userId + ")");
         // Only keep marching forward if user is actually unlocked
         if (!StorageManager.isUserKeyUnlocked(userId)) return false;
         synchronized (mLock) {
@@ -451,6 +452,7 @@
      */
     void finishUserUnlocked(final UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
+        Slog.d(TAG, "UserController event: finishUserUnlocked(" + userId + ")");
         // Only keep marching forward if user is actually unlocked
         if (!StorageManager.isUserKeyUnlocked(userId)) return;
         synchronized (mLock) {
@@ -521,6 +523,7 @@
 
     private void finishUserUnlockedCompleted(UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
+        Slog.d(TAG, "UserController event: finishUserUnlockedCompleted(" + userId + ")");
         synchronized (mLock) {
             // Bail if we ended up with a stale user
             if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
@@ -730,6 +733,7 @@
     }
 
     void finishUserStopping(final int userId, final UserState uss) {
+        Slog.d(TAG, "UserController event: finishUserStopping(" + userId + ")");
         // On to the next.
         final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
         // This is the result receiver for the final shutdown broadcast.
@@ -769,6 +773,7 @@
 
     void finishUserStopped(UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
+        Slog.d(TAG, "UserController event: finishUserStopped(" + userId + ")");
         final boolean stopped;
         boolean lockUser = true;
         final ArrayList<IStopUserCallback> stopCallbacks;
@@ -1280,6 +1285,7 @@
     boolean unlockUser(final @UserIdInt int userId, byte[] token, byte[] secret,
             IProgressListener listener) {
         checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "unlockUser");
+        Slog.i(TAG, "unlocking user " + userId);
         final long binderToken = Binder.clearCallingIdentity();
         try {
             return unlockUserCleared(userId, token, secret, listener);
@@ -1364,6 +1370,7 @@
 
     boolean switchUser(final int targetUserId) {
         enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId);
+        Slog.i(TAG, "switching to user " + targetUserId);
         int currentUserId = getCurrentUserId();
         UserInfo targetUserInfo = getUserInfo(targetUserId);
         if (targetUserId == currentUserId) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 14f9654..0b74840 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -17,11 +17,12 @@
 package com.android.server.appop;
 
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
-import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE;
-import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
+import static android.app.AppOpsManager.NoteOpEvent;
+import static android.app.AppOpsManager.OpEventProxyInfo;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
+import static android.app.AppOpsManager.OP_FLAG_SELF;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -33,6 +34,9 @@
 import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
 import static android.app.AppOpsManager.UID_STATE_TOP;
 import static android.app.AppOpsManager._NUM_OP;
+import static android.app.AppOpsManager.extractFlagsFromKey;
+import static android.app.AppOpsManager.extractUidStateFromKey;
+import static android.app.AppOpsManager.makeKey;
 import static android.app.AppOpsManager.modeToName;
 import static android.app.AppOpsManager.opToName;
 import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
@@ -93,7 +97,6 @@
 import android.util.AtomicFile;
 import android.util.KeyValueListParser;
 import android.util.LongSparseArray;
-import android.util.LongSparseLongArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -365,8 +368,6 @@
         public long pendingStateCommitTime;
         public int capability;
         public int pendingCapability;
-        // For all features combined
-        public int startNesting;
 
         public ArrayMap<String, Ops> pkgOps;
         public SparseIntArray opModes;
@@ -474,126 +475,318 @@
         }
     }
 
-    private static final class FeatureOp {
+    /** A in progress startOp->finishOp event */
+    private static final class InProgressStartOpEvent implements IBinder.DeathRecipient {
+        /** Time of startOp event */
+        final long startTime;
+
+        /** Id of the client that started the event */
+        final @NonNull IBinder clientId;
+
+        /** To call when client dies */
+        final @NonNull Runnable onDeath;
+
+        /** uidstate used when calling startOp */
+        final @AppOpsManager.UidState int uidState;
+
+        /** How many times the op was started but not finished yet */
+        int numUnfinishedStarts;
+
+        private InProgressStartOpEvent(long startTime, @NonNull IBinder clientId,
+                @NonNull Runnable onDeath, int uidState) throws RemoteException {
+            this.startTime = startTime;
+            this.clientId = clientId;
+            this.onDeath = onDeath;
+            this.uidState = uidState;
+
+            clientId.linkToDeath(this, 0);
+        }
+
+        /** Clean up event */
+        public void finish() {
+            clientId.unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            onDeath.run();
+        }
+    }
+
+    private final class FeatureOp {
         public final @NonNull Op parent;
 
-        public boolean running;
+        /**
+         * Last successful accesses (noteOp + finished startOp) for each uidState/opFlag combination
+         *
+         * <p>Key is {@link AppOpsManager#makeKey}
+         */
+        @GuardedBy("AppOpsService.this")
+        private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mAccessEvents;
 
-        private @Nullable LongSparseLongArray mAccessTimes;
-        private @Nullable LongSparseLongArray mRejectTimes;
-        private @Nullable LongSparseLongArray mDurations;
-        private @Nullable LongSparseLongArray mProxyUids;
-        private @Nullable LongSparseArray<String> mProxyFeatureIds;
-        private @Nullable LongSparseArray<String> mProxyPackageNames;
+        /**
+         * Last rejected accesses for each uidState/opFlag combination
+         *
+         * <p>Key is {@link AppOpsManager#makeKey}
+         */
+        @GuardedBy("AppOpsService.this")
+        private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mRejectEvents;
 
-        public int startNesting;
-        public long startRealtime;
+        /**
+         * Currently in progress startOp events
+         *
+         * <p>Key is clientId
+         */
+        @GuardedBy("AppOpsService.this")
+        private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
 
         FeatureOp(@NonNull Op parent) {
             this.parent = parent;
         }
 
-        public void accessed(long time, int proxyUid, @Nullable String proxyPackageName,
+        /**
+         * Update state when noteOp was rejected or startOp->finishOp event finished
+         *
+         * @param proxyUid The uid of the proxy
+         * @param proxyPackageName The package name of the proxy
+         * @param proxyFeatureId the featureId in the proxies package
+         * @param uidState UID state of the app noteOp/startOp was called for
+         * @param flags OpFlags of the call
+         */
+        public void accessed(int proxyUid, @Nullable String proxyPackageName,
                 @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState,
                 @OpFlags int flags) {
-            final long key = AppOpsManager.makeKey(uidState, flags);
-            if (mAccessTimes == null) {
-                mAccessTimes = new LongSparseLongArray();
-            }
-            mAccessTimes.put(key, time);
-            updateProxyState(key, proxyUid, proxyPackageName, proxyFeatureId);
-            if (mDurations != null) {
-                mDurations.delete(key);
-            }
+            accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName,
+                    proxyFeatureId, uidState, flags);
         }
 
-        public void rejected(long time, int proxyUid, @Nullable String proxyPackageName,
-                @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState,
-                @OpFlags int flags) {
-            final long key = AppOpsManager.makeKey(uidState, flags);
-            if (mRejectTimes == null) {
-                mRejectTimes = new LongSparseLongArray();
-            }
-            mRejectTimes.put(key, time);
-            updateProxyState(key, proxyUid, proxyPackageName, proxyFeatureId);
-            if (mDurations != null) {
-                mDurations.delete(key);
-            }
-        }
-
-        public void started(long time, @AppOpsManager.UidState int uidState, @OpFlags int flags) {
-            updateAccessTimeAndDuration(time, -1 /*duration*/, uidState, flags);
-            running = true;
-        }
-
-        public void finished(long time, long duration, @AppOpsManager.UidState int uidState,
-                @OpFlags int flags) {
-            updateAccessTimeAndDuration(time, duration, uidState, flags);
-            running = false;
-        }
-
-        public void running(long time, long duration, @AppOpsManager.UidState int uidState,
-                @OpFlags int flags) {
-            updateAccessTimeAndDuration(time, duration, uidState, flags);
-        }
-
-        public void continuing(long duration, @AppOpsManager.UidState int uidState,
-                @OpFlags int flags) {
-            final long key = AppOpsManager.makeKey(uidState, flags);
-            if (mDurations == null) {
-                mDurations = new LongSparseLongArray();
-            }
-            mDurations.put(key, duration);
-        }
-
-        private void updateAccessTimeAndDuration(long time, long duration,
+        /**
+         * Add an access that was previously collected.
+         *
+         * @param noteTime The time of the event
+         * @param duration The duration of the event
+         * @param proxyUid The uid of the proxy
+         * @param proxyPackageName The package name of the proxy
+         * @param proxyFeatureId the featureId in the proxies package
+         * @param uidState UID state of the app noteOp/startOp was called for
+         * @param flags OpFlags of the call
+         */
+        public void accessed(long noteTime, long duration, int proxyUid,
+                @Nullable String proxyPackageName, @Nullable String proxyFeatureId,
                 @AppOpsManager.UidState int uidState, @OpFlags int flags) {
-            final long key = AppOpsManager.makeKey(uidState, flags);
-            if (mAccessTimes == null) {
-                mAccessTimes = new LongSparseLongArray();
+            long key = makeKey(uidState, flags);
+
+            if (mAccessEvents == null) {
+                mAccessEvents = new LongSparseArray<>(1);
             }
-            mAccessTimes.put(key, time);
-            if (mDurations == null) {
-                mDurations = new LongSparseLongArray();
+
+            OpEventProxyInfo proxyInfo = null;
+            if (proxyUid != Process.INVALID_UID) {
+                proxyInfo = new OpEventProxyInfo(proxyUid, proxyPackageName, proxyFeatureId);
             }
-            mDurations.put(key, duration);
+            mAccessEvents.put(key, new NoteOpEvent(noteTime, duration, proxyInfo));
         }
 
-        private void updateProxyState(long key, int proxyUid,
-                @Nullable String proxyPackageName, @Nullable String featureId) {
-            if (proxyUid == Process.INVALID_UID) {
+        /**
+         * Update state when noteOp/startOp was rejected.
+         *
+         * @param uidState UID state of the app noteOp is called for
+         * @param flags OpFlags of the call
+         */
+        public void rejected(@AppOpsManager.UidState int uidState, @OpFlags int flags) {
+            rejected(System.currentTimeMillis(), uidState, flags);
+        }
+
+        /**
+         * Add an rejection that was previously collected
+         *
+         * @param noteTime The time of the event
+         * @param uidState UID state of the app noteOp/startOp was called for
+         * @param flags OpFlags of the call
+         */
+        public void rejected(long noteTime, @AppOpsManager.UidState int uidState,
+                @OpFlags int flags) {
+            long key = makeKey(uidState, flags);
+
+            if (mRejectEvents == null) {
+                mRejectEvents = new LongSparseArray<>(1);
+            }
+
+            // We do not collect proxy information for rejections yet
+            mRejectEvents.put(key, new NoteOpEvent(noteTime, -1, null));
+        }
+
+        /**
+         * Update state when start was called
+         *
+         * @param clientId Id of the startOp caller
+         * @param uidState UID state of the app startOp is called for
+         */
+        public void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState)
+                throws RemoteException {
+            if (!parent.isRunning()) {
+                scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+                        parent.packageName, true);
+            }
+
+            if (mInProgressEvents == null) {
+                mInProgressEvents = new ArrayMap<>(1);
+            }
+
+
+            InProgressStartOpEvent event = mInProgressEvents.get(clientId);
+            if (event == null) {
+                event = new InProgressStartOpEvent(System.currentTimeMillis(), clientId, () -> {
+                    // In the case the client dies without calling finish first
+                    synchronized (AppOpsService.this) {
+                        if (mInProgressEvents == null) {
+                            return;
+                        }
+
+                        InProgressStartOpEvent deadEvent = mInProgressEvents.get(clientId);
+                        if (deadEvent != null) {
+                            deadEvent.numUnfinishedStarts = 1;
+                        }
+
+                        finished(clientId);
+                    }
+                }, uidState);
+                mInProgressEvents.put(clientId, event);
+            } else {
+                if (uidState != event.uidState) {
+                    onUidStateChanged(uidState);
+                }
+            }
+
+            event.numUnfinishedStarts++;
+
+            // startOp events don't support proxy, hence use flags==SELF
+            mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
+                    uidState, OP_FLAG_SELF);
+        }
+
+        /**
+         * Update state when finishOp was called
+         *
+         * @param clientId Id of the finishOp caller
+         */
+        public void finished(@NonNull IBinder clientId) {
+            finished(clientId, true);
+        }
+
+        private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) {
+            if (mInProgressEvents == null) {
+                Slog.wtf(TAG, "No ops running");
                 return;
             }
 
-            if (mProxyUids == null) {
-                mProxyUids = new LongSparseLongArray();
+            int indexOfToken = mInProgressEvents.indexOfKey(clientId);
+            if (indexOfToken < 0) {
+                Slog.wtf(TAG, "No op running for the client");
+                return;
             }
-            mProxyUids.put(key, proxyUid);
 
-            if (mProxyPackageNames == null) {
-                mProxyPackageNames = new LongSparseArray<>();
-            }
-            mProxyPackageNames.put(key, proxyPackageName);
+            InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
+            event.numUnfinishedStarts--;
+            if (event.numUnfinishedStarts == 0) {
+                event.finish();
+                mInProgressEvents.removeAt(indexOfToken);
 
-            if (mProxyFeatureIds == null) {
-                mProxyFeatureIds = new LongSparseArray<>();
+                if (mAccessEvents == null) {
+                    mAccessEvents = new LongSparseArray<>(1);
+                }
+
+                // startOp events don't support proxy, hence use flags==SELF
+                NoteOpEvent finishedEvent = new NoteOpEvent(event.startTime,
+                        System.currentTimeMillis() - event.startTime, null);
+                mAccessEvents.put(makeKey(event.uidState, OP_FLAG_SELF), finishedEvent);
+
+                mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
+                        parent.packageName, event.uidState, AppOpsManager.OP_FLAG_SELF,
+                        finishedEvent.duration);
+
+                if (mInProgressEvents.isEmpty()) {
+                    mInProgressEvents = null;
+
+                    // TODO moltmann: Also callback for single feature activity changes
+                    if (triggerCallbackIfNeeded && !parent.isRunning()) {
+                        scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+                                parent.packageName, false);
+                    }
+                }
             }
-            mProxyFeatureIds.put(key, featureId);
+        }
+
+        /**
+         * Notify that the state of the uid changed
+         *
+         * @param newState The new state
+         */
+        public void onUidStateChanged(@AppOpsManager.UidState int newState) {
+            if (mInProgressEvents == null) {
+                return;
+            }
+
+            int numInProgressEvents = mInProgressEvents.size();
+            for (int i = 0; i < numInProgressEvents; i++) {
+                InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+
+                if (event.uidState != newState) {
+                    try {
+                        finished(event.clientId, false);
+                        started(event.clientId, newState);
+                    } catch (RemoteException e) {
+                        if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
+                    }
+                }
+            }
+        }
+
+        public boolean isRunning() {
+            return mInProgressEvents != null;
         }
 
         boolean hasAnyTime() {
-            return (mAccessTimes != null && mAccessTimes.size() > 0)
-                    || (mRejectTimes != null && mRejectTimes.size() > 0);
+            return (mAccessEvents != null && mAccessEvents.size() > 0)
+                    || (mRejectEvents != null && mRejectEvents.size() > 0);
         }
 
-        @NonNull OpFeatureEntry.Builder createFeatureEntryBuilderLocked() {
-            return new OpFeatureEntry.Builder(running, mAccessTimes, mRejectTimes, mDurations,
-                    mProxyUids, mProxyPackageNames, mProxyFeatureIds);
+        @NonNull OpFeatureEntry createFeatureEntryLocked() {
+            LongSparseArray<NoteOpEvent> accessEvents = null;
+            if (mAccessEvents != null) {
+                accessEvents = mAccessEvents.clone();
+            }
+
+            // Add in progress events as access events
+            if (mInProgressEvents != null) {
+                long now = System.currentTimeMillis();
+                int numInProgressEvents = mInProgressEvents.size();
+
+                if (accessEvents == null) {
+                    accessEvents = new LongSparseArray<>(numInProgressEvents);
+                }
+
+                for (int i = 0; i < numInProgressEvents; i++) {
+                    InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+
+                    // startOp events don't support proxy
+                    accessEvents.append(makeKey(event.uidState, OP_FLAG_SELF),
+                            new NoteOpEvent(event.startTime, now - event.startTime, null));
+                }
+            }
+
+            LongSparseArray<NoteOpEvent> rejectEvents = null;
+            if (mRejectEvents != null) {
+                rejectEvents = mRejectEvents.clone();
+            }
+
+            return new OpFeatureEntry(parent.op, isRunning(), accessEvents, rejectEvents);
         }
     }
 
-    final static class Op {
+    final class Op {
         int op;
+        int uid;
         final UidState uidState;
         final @NonNull String packageName;
 
@@ -602,8 +795,9 @@
         /** featureId -> FeatureOp */
         final ArrayMap<String, FeatureOp> mFeatures = new ArrayMap<>(1);
 
-        Op(UidState uidState, String packageName, int op) {
+        Op(UidState uidState, String packageName, int op, int uid) {
             this.op = op;
+            this.uid = uid;
             this.uidState = uidState;
             this.packageName = packageName;
             this.mode = AppOpsManager.opToDefaultMode(op);
@@ -641,11 +835,10 @@
         @NonNull OpEntry createEntryLocked() {
             final int numFeatures = mFeatures.size();
 
-            final Pair<String, OpFeatureEntry.Builder>[] featureEntries =
-                    new Pair[numFeatures];
+            final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(numFeatures);
             for (int i = 0; i < numFeatures; i++) {
-                featureEntries[i] = new Pair<>(mFeatures.keyAt(i),
-                        mFeatures.valueAt(i).createFeatureEntryBuilderLocked());
+                featureEntries.put(mFeatures.keyAt(i),
+                        mFeatures.valueAt(i).createFeatureEntryLocked());
             }
 
             return new OpEntry(op, mode, featureEntries);
@@ -654,18 +847,28 @@
         @NonNull OpEntry createSingleFeatureEntryLocked(@Nullable String featureId) {
             final int numFeatures = mFeatures.size();
 
-            final Pair<String, AppOpsManager.OpFeatureEntry.Builder>[] featureEntries =
-                    new Pair[1];
+            final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(1);
             for (int i = 0; i < numFeatures; i++) {
                 if (Objects.equals(mFeatures.keyAt(i), featureId)) {
-                    featureEntries[0] = new Pair<>(mFeatures.keyAt(i),
-                            mFeatures.valueAt(i).createFeatureEntryBuilderLocked());
+                    featureEntries.put(mFeatures.keyAt(i),
+                            mFeatures.valueAt(i).createFeatureEntryLocked());
                     break;
                 }
             }
 
             return new OpEntry(op, mode, featureEntries);
         }
+
+        boolean isRunning() {
+            final int numFeatures = mFeatures.size();
+            for (int i = 0; i < numFeatures; i++) {
+                if (mFeatures.valueAt(i).isRunning()) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
     }
 
     final SparseArray<ArraySet<ModeCallback>> mOpModeWatchers = new SparseArray<>();
@@ -815,53 +1018,6 @@
         }
     }
 
-    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
-
-    final class ClientState extends Binder implements DeathRecipient {
-        final ArrayList<Pair<Op, String>> mStartedOps = new ArrayList<>();
-        final IBinder mAppToken;
-        final int mPid;
-
-        ClientState(IBinder appToken) {
-            mAppToken = appToken;
-            mPid = Binder.getCallingPid();
-            // Watch only for remote processes dying
-            if (!(appToken instanceof Binder)) {
-                try {
-                    mAppToken.linkToDeath(this, 0);
-                } catch (RemoteException e) {
-                    /* do nothing */
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "ClientState{" +
-                    "mAppToken=" + mAppToken +
-                    ", " + "pid=" + mPid +
-                    '}';
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (AppOpsService.this) {
-                for (int i=mStartedOps.size()-1; i>=0; i--) {
-                    final Pair<Op, String> startedOp = mStartedOps.get(i);
-                    final Op op = startedOp.first;
-                    final String featureId = startedOp.second;
-
-                    finishOperationLocked(op, featureId, /*finishNested*/ true);
-                    if (op.mFeatures.get(featureId).startNesting <= 0) {
-                        scheduleOpActiveChangedIfNeededLocked(op.op, op.uidState.uid,
-                                op.packageName, false);
-                    }
-                }
-                mClients.remove(mAppToken);
-            }
-        }
-    }
-
     public AppOpsService(File storagePath, Handler handler) {
         LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
         mFile = new AtomicFile(storagePath, "appops");
@@ -1024,43 +1180,19 @@
                 mUidStates.remove(uid);
             }
 
-            // Finish ops other packages started on behalf of the package.
-            final int clientCount = mClients.size();
-            for (int i = 0; i < clientCount; i++) {
-                final ClientState client = mClients.valueAt(i);
-                if (client.mStartedOps == null) {
-                    continue;
-                }
-                final int startedOpCount = client.mStartedOps.size();
-                for (int j = startedOpCount - 1; j >= 0; j--) {
-                    final Pair<Op, String> startedOp = client.mStartedOps.get(j);
-                    final Op op = startedOp.first;
-                    final String featureId = startedOp.second;
-
-                    if (uid == op.uidState.uid && packageName.equals(op.packageName)) {
-                        finishOperationLocked(op, featureId, /*finishNested*/ true);
-                        client.mStartedOps.remove(j);
-                        if (op.mFeatures.get(featureId).startNesting <= 0) {
-                            scheduleOpActiveChangedIfNeededLocked(op.op,
-                                    uid, packageName, false);
-                        }
-                    }
-                }
-            }
-
             if (ops != null) {
                 scheduleFastWriteLocked();
 
-                final int opCount = ops.size();
-                for (int opNum = 0; opNum < opCount; opNum++) {
+                final int numOps = ops.size();
+                for (int opNum = 0; opNum < numOps; opNum++) {
                     final Op op = ops.valueAt(opNum);
 
                     final int numFeatures = op.mFeatures.size();
                     for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
-                        if (op.mFeatures.valueAt(featureNum).running) {
-                            scheduleOpActiveChangedIfNeededLocked(
-                                    op.op, op.uidState.uid, op.packageName, false);
-                            break;
+                        FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
+
+                        while (featureOp.mInProgressEvents != null) {
+                            featureOp.mInProgressEvents.valueAt(0).onDeath.run();
                         }
                     }
                 }
@@ -1112,35 +1244,21 @@
                     }
                     uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
                 }
-                if (uidState.startNesting != 0) {
-                    // There is some actively running operation...  need to find it
-                    // and appropriately update its state.
-                    final long now = System.currentTimeMillis();
-                    final long nowElapsed = SystemClock.elapsedRealtime();
-                    for (int i = uidState.pkgOps.size() - 1; i >= 0; i--) {
-                        final Ops ops = uidState.pkgOps.valueAt(i);
-                        for (int j = ops.size() - 1; j >= 0; j--) {
-                            final Op op = ops.valueAt(j);
+
+                if (uidState.pkgOps != null) {
+                    int numPkgs = uidState.pkgOps.size();
+                    for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+                        Ops ops = uidState.pkgOps.valueAt(pkgNum);
+
+                        int numOps = ops.size();
+                        for (int opNum = 0; opNum < numOps; opNum++) {
+                            Op op = ops.valueAt(opNum);
 
                             int numFeatures = op.mFeatures.size();
-                            for (int featureNum = 0; featureNum < numFeatures;
-                                    featureNum++) {
-                                final FeatureOp featureOp = op.mFeatures.valueAt(
-                                        featureNum);
-                                if (featureOp.startNesting > 0) {
-                                    final long duration = SystemClock.elapsedRealtime()
-                                            - featureOp.startRealtime;
-                                    // We don't support proxy long running ops (start/stop)
-                                    mHistoricalRegistry.increaseOpAccessDuration(op.op,
-                                            op.uidState.uid, op.packageName, oldPendingState,
-                                            AppOpsManager.OP_FLAG_SELF, duration);
-                                    // Finish the op in the old state
-                                    featureOp.finished(now, duration, oldPendingState,
-                                            AppOpsManager.OP_FLAG_SELF);
-                                    // Start the op in the new state
-                                    featureOp.startRealtime = nowElapsed;
-                                    featureOp.started(now, newState, AppOpsManager.OP_FLAG_SELF);
-                                }
+                            for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+                                FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
+
+                                featureOp.onUidStateChanged(newState);
                             }
                         }
                     }
@@ -1202,7 +1320,7 @@
             resOps = new ArrayList<>();
             for (int i = 0; i < opModeCount; i++) {
                 int code = uidState.opModes.keyAt(i);
-                resOps.add(new OpEntry(code, uidState.opModes.get(code), new Pair[0]));
+                resOps.add(new OpEntry(code, uidState.opModes.get(code), Collections.emptyMap()));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
@@ -1211,7 +1329,8 @@
                     if (resOps == null) {
                         resOps = new ArrayList<>();
                     }
-                    resOps.add(new OpEntry(code, uidState.opModes.get(code), new Pair[0]));
+                    resOps.add(new OpEntry(code, uidState.opModes.get(code),
+                            Collections.emptyMap()));
                 }
             }
         }
@@ -1219,16 +1338,6 @@
     }
 
     private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) {
-        final int numFeatures = op.mFeatures.size();
-
-        for (int i = 0; i < numFeatures; i++) {
-            final FeatureOp featureOp = op.mFeatures.valueAt(i);
-            if (featureOp.running) {
-                featureOp.continuing(elapsedNow - featureOp.startRealtime,
-                        op.uidState.state, AppOpsManager.OP_FLAG_SELF);
-            }
-        }
-
         return op.createEntryLocked();
     }
 
@@ -1962,18 +2071,6 @@
         }
     }
 
-    @Override
-    public IBinder getToken(IBinder clientToken) {
-        synchronized (this) {
-            ClientState cs = mClients.get(clientToken);
-            if (cs == null) {
-                cs = new ClientState(clientToken);
-                mClients.put(clientToken, cs);
-            }
-            return cs;
-        }
-    }
-
     public CheckOpsDelegate getAppOpsServiceDelegate() {
         synchronized (this) {
             return mCheckOpsDelegate;
@@ -2214,15 +2311,10 @@
                 return AppOpsManager.MODE_IGNORED;
             }
             final UidState uidState = ops.uidState;
-            if (featureOp.running) {
-                final OpFeatureEntry entry = getOpLocked(ops, code,
-                        false).createSingleFeatureEntryLocked(featureId).getFeatures().get(
-                        featureId);
-
-                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
-                        + " code " + code + " time=" + entry.getLastAccessTime(uidState.state,
-                        uidState.state, flags) + " duration=" + entry.getLastDuration(
-                                uidState.state, uidState.state, flags));
+            if (featureOp.isRunning()) {
+                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
+                        + code + " startTime of in progress event="
+                        + featureOp.mInProgressEvents.valueAt(0).startTime);
             }
 
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -2234,8 +2326,7 @@
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
-                    featureOp.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
-                            proxyFeatureId, uidState.state, flags);
+                    featureOp.rejected(uidState.state, flags);
                     mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
                             uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
@@ -2248,8 +2339,7 @@
                     if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
-                    featureOp.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
-                            proxyFeatureId, uidState.state, flags);
+                    featureOp.rejected(uidState.state, flags);
                     mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
                             uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
@@ -2258,8 +2348,7 @@
             }
             if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                     + " package " + packageName + (featureId == null ? "" : "." + featureId));
-            featureOp.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName,
-                    proxyFeatureId, uidState.state, flags);
+            featureOp.accessed(proxyUid, proxyPackageName, proxyFeatureId, uidState.state, flags);
             // TODO moltmann: Add features to historical app-ops
             mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
                     uidState.state, flags);
@@ -2496,7 +2585,7 @@
     }
 
     @Override
-    public int startOperation(IBinder token, int code, int uid, String packageName,
+    public int startOperation(IBinder clientId, int code, int uid, String packageName,
             String featureId, boolean startIfModeDefault) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -2504,7 +2593,6 @@
         if (resolvedPackageName == null) {
             return  AppOpsManager.MODE_IGNORED;
         }
-        ClientState client = (ClientState)token;
 
         boolean isPrivileged;
         try {
@@ -2539,10 +2627,7 @@
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
-                    // We don't support proxy long running ops (start/stop)
-                    featureOp.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
-                            null /*proxyPackage*/, null, uidState.state,
-                            AppOpsManager.OP_FLAG_SELF);
+                    featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
                     mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
                             uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return uidMode;
@@ -2555,10 +2640,7 @@
                     if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
-                    // We don't support proxy long running ops (start/stop)
-                    featureOp.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
-                            null /*proxyPackage*/, null, uidState.state,
-                            AppOpsManager.OP_FLAG_SELF);
+                    featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
                     mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
                             uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return mode;
@@ -2566,29 +2648,19 @@
             }
             if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
                     + " package " + resolvedPackageName);
-            if (featureOp.startNesting == 0) {
-                featureOp.startRealtime = SystemClock.elapsedRealtime();
-                // We don't support proxy long running ops (start/stop)
-                featureOp.started(System.currentTimeMillis(), uidState.state,
-                        AppOpsManager.OP_FLAG_SELF);
-                mHistoricalRegistry.incrementOpAccessedCount(opCode, uid, packageName,
-                        uidState.state, AppOpsManager.OP_FLAG_SELF);
 
-                // TODO moltmann: call back when a feature became inactive
-                if (uidState.startNesting == 0) {
-                    scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
-                }
+            try {
+                featureOp.started(clientId, uidState.state);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
             }
-            featureOp.startNesting++;
-            uidState.startNesting++;
-            client.mStartedOps.add(new Pair<>(op, featureId));
         }
 
         return AppOpsManager.MODE_ALLOWED;
     }
 
     @Override
-    public void finishOperation(IBinder token, int code, int uid, String packageName,
+    public void finishOperation(IBinder clientId, int code, int uid, String packageName,
             String featureId) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -2596,10 +2668,6 @@
         if (resolvedPackageName == null) {
             return;
         }
-        if (!(token instanceof ClientState)) {
-            return;
-        }
-        ClientState client = (ClientState) token;
 
         boolean isPrivileged;
         try {
@@ -2619,36 +2687,13 @@
                 return;
             }
 
-            if (client.mStartedOps.remove(new Pair<>(op, featureId))) {
-                finishOperationLocked(op, featureId, /*finishNested*/ false);
-
-                // TODO moltmann: call back when a feature became inactive
-                if (op.uidState.startNesting <= 0) {
-                    scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
-                }
-
-                return;
+            try {
+                featureOp.finished(clientId);
+            } catch (IllegalStateException e) {
+                Slog.e(TAG, "Operation not started: uid=" + uid + " pkg="
+                        + packageName + " op=" + AppOpsManager.opToName(code), e);
             }
         }
-
-        // We finish ops when packages get removed to guarantee no dangling
-        // started ops. However, some part of the system may asynchronously
-        // finish ops for an already gone package. Hence, finishing an op
-        // for a non existing package is fine and we don't log as a wtf.
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            if (LocalServices.getService(PackageManagerInternal.class).getPackageUid(
-                    resolvedPackageName, 0, UserHandle.getUserId(uid)) < 0) {
-                Slog.i(TAG, "Finishing op=" + AppOpsManager.opToName(code)
-                        + " for non-existing package=" + resolvedPackageName
-                        + " in uid=" + uid);
-                return;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        Slog.wtf(TAG, "Operation not started: uid=" + uid + " pkg="
-                + packageName + " op=" + AppOpsManager.opToName(code));
     }
 
     private void scheduleOpActiveChangedIfNeededLocked(int code, int uid, String packageName,
@@ -2769,38 +2814,6 @@
         return permInfo.getProtection() == PROTECTION_DANGEROUS;
     }
 
-    void finishOperationLocked(@NonNull Op op, @Nullable String featureId, boolean finishNested) {
-        final FeatureOp featureOp = op.mFeatures.get(featureId);
-        final int opCode = featureOp.parent.op;
-        final int uid = featureOp.parent.uidState.uid;
-        if (featureOp.startNesting <= 1 || finishNested) {
-            if (featureOp.startNesting == 1 || finishNested) {
-                // We don't support proxy long running ops (start/stop)
-                final long duration = SystemClock.elapsedRealtime() - featureOp.startRealtime;
-                featureOp.finished(System.currentTimeMillis(), duration, op.uidState.state,
-                        AppOpsManager.OP_FLAG_SELF);
-                mHistoricalRegistry.increaseOpAccessDuration(opCode, uid, op.packageName,
-                        op.uidState.state, AppOpsManager.OP_FLAG_SELF, duration);
-            } else {
-                final OpFeatureEntry entry = op.createSingleFeatureEntryLocked(
-                        featureId).getFeatures().get(featureId);
-                Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg "
-                        + op.packageName + " code " + opCode + " time="
-                        + entry.getLastAccessTime(OP_FLAGS_ALL)
-                        + " duration=" + entry.getLastDuration(MAX_PRIORITY_UID_STATE,
-                        MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL) + " nesting="
-                        + featureOp.startNesting);
-            }
-            if (featureOp.startNesting >= 1) {
-                op.uidState.startNesting -= featureOp.startNesting;
-            }
-            featureOp.startNesting = 0;
-        } else {
-            featureOp.startNesting--;
-            op.uidState.startNesting--;
-        }
-    }
-
     private void verifyIncomingUid(int uid) {
         if (uid == Binder.getCallingUid()) {
             return;
@@ -3064,7 +3077,7 @@
             if (!edit) {
                 return null;
             }
-            op = new Op(ops.uidState, ops.packageName, code);
+            op = new Op(ops.uidState, ops.packageName, code, ops.uidState.uid);
             ops.put(code, op);
         }
         if (edit) {
@@ -3208,7 +3221,7 @@
                     final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
                     if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
                         final Op copy = new Op(op.uidState, op.packageName,
-                            AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
+                                AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.uid);
                         copy.mode = op.mode;
                         ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
                         changed = true;
@@ -3336,25 +3349,22 @@
         final FeatureOp featureOp = parent.getOrCreateFeature(parent, feature);
 
         final long key = XmlUtils.readLongAttribute(parser, "n");
-
-        final int flags = AppOpsManager.extractFlagsFromKey(key);
-        final int state = AppOpsManager.extractUidStateFromKey(key);
+        final int uidState = extractUidStateFromKey(key);
+        final int opFlags = extractFlagsFromKey(key);
 
         final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0);
         final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0);
-        final long accessDuration = XmlUtils.readLongAttribute(parser, "d", 0);
+        final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1);
         final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
-        final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", 0);
+        final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID);
         final String proxyFeatureId = XmlUtils.readStringAttribute(parser, "pc");
 
         if (accessTime > 0) {
-            featureOp.accessed(accessTime, proxyUid, proxyPkg, proxyFeatureId, state, flags);
+            featureOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg, proxyFeatureId,
+                    uidState, opFlags);
         }
         if (rejectTime > 0) {
-            featureOp.rejected(rejectTime, proxyUid, proxyPkg, proxyFeatureId, state, flags);
-        }
-        if (accessDuration > 0) {
-            featureOp.running(accessTime, accessDuration, state, flags);
+            featureOp.rejected(rejectTime, uidState, opFlags);
         }
     }
 
@@ -3362,7 +3372,8 @@
         @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
         XmlPullParserException, IOException {
         Op op = new Op(uidState, pkgName,
-                Integer.parseInt(parser.getAttributeValue(null, "n")));
+                Integer.parseInt(parser.getAttributeValue(null, "n")),
+                uidState.uid);
 
         final int mode = XmlUtils.readIntAttribute(parser, "m",
                 AppOpsManager.opToDefaultMode(op.op));
@@ -3496,35 +3507,39 @@
                                 final OpFeatureEntry feature = op.getFeatures().get(
                                         featureId);
 
-                                final LongSparseArray keys = feature.collectKeys();
-                                if (keys == null || keys.size() <= 0) {
-                                    continue;
-                                }
-                                final int keyCount = keys.size();
+                                final ArraySet<Long> keys = feature.collectKeys();
 
+                                final int keyCount = keys.size();
                                 for (int k = 0; k < keyCount; k++) {
-                                    final long key = keys.keyAt(k);
+                                    final long key = keys.valueAt(k);
 
                                     final int uidState = AppOpsManager.extractUidStateFromKey(key);
                                     final int flags = AppOpsManager.extractFlagsFromKey(key);
 
-                                    final long accessTime = feature.getLastAccessTime(
-                                            uidState, uidState, flags);
-                                    final long rejectTime = feature.getLastRejectTime(
-                                            uidState, uidState, flags);
-                                    final long accessDuration = feature.getLastDuration(
-                                            uidState, uidState, flags);
-                                    final String proxyPkg = feature.getProxyPackageName(uidState,
-                                            flags);
-                                    final String proxyFeatureId = feature.getProxyFeatureId(
+                                    final long accessTime = feature.getLastAccessTime(uidState,
                                             uidState, flags);
-                                    final int proxyUid = feature.getProxyUid(uidState, flags);
+                                    final long rejectTime = feature.getLastRejectTime(uidState,
+                                            uidState, flags);
+                                    final long accessDuration = feature.getLastDuration(uidState,
+                                            uidState, flags);
+                                    // Proxy information for rejections is not backed up
+                                    final OpEventProxyInfo proxy = feature.getLastProxyInfo(
+                                            uidState, uidState, flags);
 
                                     if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
-                                            && proxyPkg == null && proxyUid < 0) {
+                                            && proxy == null) {
                                         continue;
                                     }
 
+                                    String proxyPkg = null;
+                                    String proxyFeatureId = null;
+                                    int proxyUid = Process.INVALID_UID;
+                                    if (proxy != null) {
+                                        proxyPkg = proxy.getPackageName();
+                                        proxyFeatureId = proxy.getFeatureId();
+                                        proxyUid = proxy.getUid();
+                                    }
+
                                     out.startTag(null, "st");
                                     if (featureId != null) {
                                         out.attribute(null, "id", featureId);
@@ -3591,10 +3606,7 @@
         Shell(IAppOpsService iface, AppOpsService internal) {
             mInterface = iface;
             mInternal = internal;
-            try {
-                mToken = mInterface.getToken(sBinder);
-            } catch (RemoteException e) {
-            }
+            mToken = AppOpsManager.getClientId();
         }
 
         @Override
@@ -3889,42 +3901,48 @@
                             pw.print(": ");
                             pw.print(AppOpsManager.modeToName(ent.getMode()));
                             if (shell.featureId == null) {
-                                if (ent.getTime() != 0) {
+                                if (ent.getLastAccessTime(OP_FLAGS_ALL) != -1) {
                                     pw.print("; time=");
-                                    TimeUtils.formatDuration(now - ent.getTime(), pw);
+                                    TimeUtils.formatDuration(
+                                            now - ent.getLastAccessTime(OP_FLAGS_ALL), pw);
                                     pw.print(" ago");
                                 }
-                                if (ent.getRejectTime() != 0) {
+                                if (ent.getLastRejectTime(OP_FLAGS_ALL) != -1) {
                                     pw.print("; rejectTime=");
-                                    TimeUtils.formatDuration(now - ent.getRejectTime(), pw);
+                                    TimeUtils.formatDuration(
+                                            now - ent.getLastRejectTime(OP_FLAGS_ALL), pw);
                                     pw.print(" ago");
                                 }
-                                if (ent.getDuration() == -1) {
+                                if (ent.isRunning()) {
                                     pw.print(" (running)");
-                                } else if (ent.getDuration() != 0) {
+                                } else if (ent.getLastDuration(OP_FLAGS_ALL) != -1) {
                                     pw.print("; duration=");
-                                    TimeUtils.formatDuration(ent.getDuration(), pw);
+                                    TimeUtils.formatDuration(ent.getLastDuration(OP_FLAGS_ALL), pw);
                                 }
                             } else {
                                 final OpFeatureEntry featureEnt = ent.getFeatures().get(
                                         shell.featureId);
                                 if (featureEnt != null) {
-                                    if (featureEnt.getTime() != 0) {
+                                    if (featureEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) {
                                         pw.print("; time=");
-                                        TimeUtils.formatDuration(now - featureEnt.getTime(), pw);
+                                        TimeUtils.formatDuration(now - featureEnt.getLastAccessTime(
+                                                OP_FLAGS_ALL), pw);
                                         pw.print(" ago");
                                     }
-                                    if (featureEnt.getRejectTime() != 0) {
+                                    if (featureEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) {
                                         pw.print("; rejectTime=");
-                                        TimeUtils.formatDuration(now - featureEnt.getRejectTime(),
+                                        TimeUtils.formatDuration(
+                                                now - featureEnt.getLastRejectTime(OP_FLAGS_ALL),
                                                 pw);
                                         pw.print(" ago");
                                     }
-                                    if (featureEnt.getDuration() == -1) {
+                                    if (featureEnt.isRunning()) {
                                         pw.print(" (running)");
-                                    } else if (featureEnt.getDuration() != 0) {
+                                    } else if (featureEnt.getLastDuration(OP_FLAGS_ALL)
+                                            != -1) {
                                         pw.print("; duration=");
-                                        TimeUtils.formatDuration(featureEnt.getDuration(), pw);
+                                        TimeUtils.formatDuration(
+                                                featureEnt.getLastDuration(OP_FLAGS_ALL), pw);
                                     }
                                 }
                             }
@@ -4095,27 +4113,28 @@
         final OpFeatureEntry entry = op.createSingleFeatureEntryLocked(
                 featureId).getFeatures().get(featureId);
 
-        final LongSparseArray keys = entry.collectKeys();
-        if (keys == null || keys.size() <= 0) {
-            return;
-        }
+        final ArraySet<Long> keys = entry.collectKeys();
 
         final int keyCount = keys.size();
         for (int k = 0; k < keyCount; k++) {
-            final long key = keys.keyAt(k);
+            final long key = keys.valueAt(k);
 
             final int uidState = AppOpsManager.extractUidStateFromKey(key);
             final int flags = AppOpsManager.extractFlagsFromKey(key);
 
-            final long accessTime = entry.getLastAccessTime(
-                    uidState, uidState, flags);
-            final long rejectTime = entry.getLastRejectTime(
-                    uidState, uidState, flags);
-            final long accessDuration = entry.getLastDuration(
-                    uidState, uidState, flags);
-            final String proxyPkg = entry.getProxyPackageName(uidState, flags);
-            final String proxyFeatureId = entry.getProxyFeatureId(uidState, flags);
-            final int proxyUid = entry.getProxyUid(uidState, flags);
+            final long accessTime = entry.getLastAccessTime(uidState, uidState, flags);
+            final long rejectTime = entry.getLastRejectTime(uidState, uidState, flags);
+            final long accessDuration = entry.getLastDuration(uidState, uidState, flags);
+            final OpEventProxyInfo proxy = entry.getLastProxyInfo(uidState, uidState, flags);
+
+            String proxyPkg = null;
+            String proxyFeatureId = null;
+            int proxyUid = Process.INVALID_UID;
+            if (proxy != null) {
+                proxyPkg = proxy.getPackageName();
+                proxyFeatureId = proxy.getFeatureId();
+                proxyUid = proxy.getUid();
+            }
 
             if (accessTime > 0) {
                 pw.print(prefix);
@@ -4168,14 +4187,25 @@
         }
 
         final FeatureOp featureOp = op.mFeatures.get(featureId);
-        if (featureOp.running) {
+        if (featureOp.isRunning()) {
+            long earliestStartTime = Long.MAX_VALUE;
+            long maxNumStarts = 0;
+            int numInProgressEvents = featureOp.mInProgressEvents.size();
+            for (int i = 0; i < numInProgressEvents; i++) {
+                InProgressStartOpEvent event = featureOp.mInProgressEvents.valueAt(i);
+
+                earliestStartTime = Math.min(earliestStartTime, event.startTime);
+                maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts);
+            }
+
             pw.print(prefix + "Running start at: ");
-            TimeUtils.formatDuration(nowElapsed - featureOp.startRealtime, pw);
+            TimeUtils.formatDuration(nowElapsed - earliestStartTime, pw);
             pw.println();
-        }
-        if (featureOp.startNesting != 0) {
-            pw.print(prefix + "startNesting=");
-            pw.println(featureOp.startNesting);
+
+            if (maxNumStarts > 1) {
+                pw.print(prefix + "startNesting=");
+                pw.println(maxNumStarts);
+            }
         }
     }
 
@@ -4423,44 +4453,6 @@
                     pw.println(cb);
                 }
             }
-            if (mClients.size() > 0 && dumpMode < 0 && !dumpWatchers && !dumpHistory) {
-                needSep = true;
-                boolean printedHeader = false;
-                for (int i=0; i<mClients.size(); i++) {
-                    boolean printedClient = false;
-                    ClientState cs = mClients.valueAt(i);
-                    if (cs.mStartedOps.size() > 0) {
-                        boolean printedStarted = false;
-                        for (int j=0; j<cs.mStartedOps.size(); j++) {
-                            final Pair<Op, String> startedOp = cs.mStartedOps.get(j);
-                            final Op op = startedOp.first;
-                            if (dumpOp >= 0 && op.op != dumpOp) {
-                                continue;
-                            }
-                            if (dumpPackage != null && !dumpPackage.equals(op.packageName)) {
-                                continue;
-                            }
-                            if (!printedHeader) {
-                                pw.println("  Clients:");
-                                printedHeader = true;
-                            }
-                            if (!printedClient) {
-                                pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
-                                pw.print("      "); pw.println(cs);
-                                printedClient = true;
-                            }
-                            if (!printedStarted) {
-                                pw.println("      Started ops:");
-                                printedStarted = true;
-                            }
-                            pw.print("        "); pw.print("uid="); pw.print(op.uidState.uid);
-                            pw.print(" pkg="); pw.print(op.packageName);
-                            pw.print(" featureId="); pw.print(startedOp.second);
-                            pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
-                        }
-                    }
-                }
-            }
             if (mAudioRestrictionManager.hasActiveRestrictions() && dumpOp < 0
                     && dumpPackage != null && dumpMode < 0 && !dumpWatchers && !dumpWatchers) {
                 needSep = mAudioRestrictionManager.dump(pw) | needSep ;
@@ -4536,10 +4528,6 @@
                     TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowElapsed, pw);
                     pw.println();
                 }
-                if (uidState.startNesting != 0) {
-                    pw.print("    startNesting=");
-                    pw.println(uidState.startNesting);
-                }
                 if (uidState.foregroundOps != null && (dumpMode < 0
                         || dumpMode == AppOpsManager.MODE_FOREGROUND)) {
                     pw.println("    foregroundOps:");
@@ -4813,17 +4801,18 @@
         }
         // TODO moltmann: Allow to check for feature op activeness
         synchronized (AppOpsService.this) {
-            for (int i = mClients.size() - 1; i >= 0; i--) {
-                final ClientState client = mClients.valueAt(i);
-                for (int j = client.mStartedOps.size() - 1; j >= 0; j--) {
-                    final Pair<Op, String> startedOp = client.mStartedOps.get(j);
-                    if (startedOp.first.op == code && startedOp.first.uidState.uid == uid) {
-                        return true;
-                    }
-                }
+            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false, false);
+            if (pkgOps == null) {
+                return false;
             }
+
+            Op op = pkgOps.get(code);
+            if (op == null) {
+                return false;
+            }
+
+            return op.isRunning();
         }
-        return false;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 352d0d5..21c4784 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -241,15 +241,17 @@
                 } else {
                     makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
                 }
+            } else if (state == BluetoothProfile.STATE_CONNECTED) {
+                // device is not already connected
+                if (a2dpVolume != -1) {
+                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                            // convert index to internal representation in VolumeStreamState
+                            a2dpVolume * 10,
+                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
+                }
+                makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
+                        "onSetA2dpSinkConnectionState", a2dpCodec);
             }
-            if (a2dpVolume != -1) {
-                mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
-                        // convert index to internal representation in VolumeStreamState
-                        a2dpVolume * 10,
-                        AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
-            }
-            makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
-                    "onSetA2dpSinkConnectionState", a2dpCodec);
         }
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 335cac8..21cecc2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -310,7 +310,8 @@
         7,  // STREAM_SYSTEM_ENFORCED
         15, // STREAM_DTMF
         15, // STREAM_TTS
-        15  // STREAM_ACCESSIBILITY
+        15, // STREAM_ACCESSIBILITY
+        15  // STREAM_ASSISTANT
     };
 
     /** Minimum volume index values for audio streams */
@@ -325,7 +326,8 @@
         0,  // STREAM_SYSTEM_ENFORCED
         0,  // STREAM_DTMF
         0,  // STREAM_TTS
-        1   // STREAM_ACCESSIBILITY
+        1,  // STREAM_ACCESSIBILITY
+        0   // STREAM_ASSISTANT
     };
 
     /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
@@ -348,7 +350,8 @@
         AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
         AudioSystem.STREAM_RING,            // STREAM_DTMF
         AudioSystem.STREAM_MUSIC,           // STREAM_TTS
-        AudioSystem.STREAM_MUSIC            // STREAM_ACCESSIBILITY
+        AudioSystem.STREAM_MUSIC,           // STREAM_ACCESSIBILITY
+        AudioSystem.STREAM_MUSIC            // STREAM_ASSISTANT
     };
     private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {
         AudioSystem.STREAM_MUSIC,       // STREAM_VOICE_CALL
@@ -361,7 +364,8 @@
         AudioSystem.STREAM_MUSIC,       // STREAM_SYSTEM_ENFORCED
         AudioSystem.STREAM_MUSIC,       // STREAM_DTMF
         AudioSystem.STREAM_MUSIC,       // STREAM_TTS
-        AudioSystem.STREAM_MUSIC        // STREAM_ACCESSIBILITY
+        AudioSystem.STREAM_MUSIC,       // STREAM_ACCESSIBILITY
+        AudioSystem.STREAM_MUSIC        // STREAM_ASSISTANT
     };
     private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
         AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
@@ -374,7 +378,8 @@
         AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
         AudioSystem.STREAM_RING,            // STREAM_DTMF
         AudioSystem.STREAM_MUSIC,           // STREAM_TTS
-        AudioSystem.STREAM_MUSIC            // STREAM_ACCESSIBILITY
+        AudioSystem.STREAM_MUSIC,           // STREAM_ACCESSIBILITY
+        AudioSystem.STREAM_MUSIC            // STREAM_ASSISTANT
     };
     protected static int[] mStreamVolumeAlias;
 
@@ -394,6 +399,7 @@
         AppOpsManager.OP_AUDIO_MEDIA_VOLUME,            // STREAM_DTMF
         AppOpsManager.OP_AUDIO_MEDIA_VOLUME,            // STREAM_TTS
         AppOpsManager.OP_AUDIO_ACCESSIBILITY_VOLUME,    // STREAM_ACCESSIBILITY
+        AppOpsManager.OP_AUDIO_MEDIA_VOLUME             // STREAM_ASSISTANT
     };
 
     private final boolean mUseFixedVolume;
@@ -1253,6 +1259,9 @@
         int dtmfStreamAlias;
         final int a11yStreamAlias = sIndependentA11yVolume ?
                 AudioSystem.STREAM_ACCESSIBILITY : AudioSystem.STREAM_MUSIC;
+        final int assistantStreamAlias = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_useAssistantVolume) ?
+                AudioSystem.STREAM_ASSISTANT : AudioSystem.STREAM_MUSIC;
 
         if (mIsSingleVolume) {
             mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
@@ -1282,6 +1291,7 @@
 
         mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
         mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
+        mStreamVolumeAlias[AudioSystem.STREAM_ASSISTANT] = assistantStreamAlias;
 
         if (updateVolumes && mStreamStates != null) {
             updateDefaultVolumes();
@@ -1808,6 +1818,17 @@
             return;
         }
 
+        // If the stream is STREAM_ASSISTANT,
+        // make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.
+        if (streamType == AudioSystem.STREAM_ASSISTANT &&
+            mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                    != PackageManager.PERMISSION_GRANTED) {
+            Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
         // use stream type alias here so that streams with same alias have the same behavior,
         // including with regard to silent mode control (e.g the use of STREAM_RING below and in
         // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
@@ -2244,6 +2265,14 @@
                     + " MODIFY_PHONE_STATE  callingPackage=" + callingPackage);
             return;
         }
+        if ((streamType == AudioManager.STREAM_ASSISTANT)
+            && (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                    != PackageManager.PERMISSION_GRANTED)) {
+            Log.w(TAG, "Trying to call setStreamVolume() for STREAM_ASSISTANT without"
+                    + " MODIFY_AUDIO_ROUTING  callingPackage=" + callingPackage);
+            return;
+        }
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
                 index/*val1*/, flags/*val2*/, callingPackage));
         setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0acf7c8..c12395e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -572,6 +572,7 @@
             info.installFlags = params.installFlags;
             info.isMultiPackage = params.isMultiPackage;
             info.isStaged = params.isStaged;
+            info.rollbackDataPolicy = params.rollbackDataPolicy;
             info.parentSessionId = mParentSessionId;
             info.childSessionIds = mChildSessionIds.copyKeys();
             if (info.childSessionIds == null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cb362b0..0e9199f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -96,6 +96,8 @@
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.internal.util.ArrayUtils.emptyIfNull;
+import static com.android.internal.util.ArrayUtils.filter;
 import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -249,7 +251,6 @@
 import android.os.storage.VolumeRecord;
 import android.permission.IPermissionManager;
 import android.provider.DeviceConfig;
-import android.provider.MediaStore;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.security.KeyStore;
@@ -385,6 +386,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -16629,37 +16631,48 @@
                 + " Activities needs verification ...");
 
         int count = 0;
-
+        boolean handlesWebUris = false;
+        final boolean alreadyVerified;
         synchronized (mLock) {
             // If this is a new install and we see that we've already run verification for this
             // package, we have nothing to do: it means the state was restored from backup.
-            if (!replacing) {
-                IntentFilterVerificationInfo ivi =
-                        mSettings.getIntentFilterVerificationLPr(packageName);
-                if (ivi != null) {
-                    if (DEBUG_DOMAIN_VERIFICATION) {
-                        Slog.i(TAG, "Package " + packageName+ " already verified: status="
-                                + ivi.getStatusString());
-                    }
-                    return;
+            final IntentFilterVerificationInfo ivi =
+                    mSettings.getIntentFilterVerificationLPr(packageName);
+            alreadyVerified = (ivi != null);
+            if (!replacing && alreadyVerified) {
+                if (DEBUG_DOMAIN_VERIFICATION) {
+                    Slog.i(TAG, "Package " + packageName + " already verified: status="
+                            + ivi.getStatusString());
                 }
+                return;
             }
 
-            // If any filters need to be verified, then all need to be.
+            // If any filters need to be verified, then all need to be.  In addition, we need to
+            // know whether an updating app has any web navigation intent filters, to re-
+            // examine handling policy even if not re-verifying.
             boolean needToVerify = false;
             for (ParsedActivity a : activities) {
                 for (ParsedActivityIntentInfo filter : a.intents) {
+                    if (filter.handlesWebUris(true)) {
+                        handlesWebUris = true;
+                    }
                     if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
                             Slog.d(TAG,
                                     "Intent filter needs verification, so processing all filters");
                         }
                         needToVerify = true;
+                        // It's safe to break out here because filter.needsVerification()
+                        // can only be true if filter.handlesWebUris(true) returns true, so
+                        // we've already noted that.
                         break;
                     }
                 }
             }
 
+            // Note whether this app publishes any web navigation handling support at all,
+            // and whether there are any web-nav filters that fit the profile for running
+            // a verification pass now.
             if (needToVerify) {
                 final int verificationId = mIntentFilterVerificationToken++;
                 for (ParsedActivity a : activities) {
@@ -16677,13 +16690,23 @@
         }
 
         if (count > 0) {
+            // count > 0 means that we're running a full verification pass
             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
                     + " IntentFilter verification" + (count > 1 ? "s" : "")
                     +  " for userId:" + userId);
             mIntentFilterVerifier.startVerifications(userId);
+        } else if (alreadyVerified && handlesWebUris) {
+            // App used autoVerify in the past, no longer does, but still handles web
+            // navigation starts.
+            if (DEBUG_DOMAIN_VERIFICATION) {
+                Slog.d(TAG, "App changed web filters but no longer verifying - resetting policy");
+            }
+            synchronized (mLock) {
+                clearIntentFilterVerificationsLPw(packageName, userId);
+            }
         } else {
             if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "No filters or not all autoVerify for " + packageName);
+                Slog.d(TAG, "No web filters or no prior verify policy for " + packageName);
             }
         }
     }
@@ -19261,6 +19284,18 @@
         return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
     }
 
+    private @NonNull String[] dropNonSystemPackages(@NonNull String[] pkgNames) {
+        return emptyIfNull(filter(pkgNames, String[]::new, mIsSystemPackage), String.class);
+    }
+
+    private Predicate<String> mIsSystemPackage = (pkgName) -> {
+        if ("android".equals(pkgName)) {
+            return true;
+        }
+        AndroidPackage pkg = mPackages.get(pkgName);
+        return pkg != null && pkg.isSystem();
+    };
+
     @Override
     public String getSystemCaptionsServicePackageName() {
         String flattenedSystemCaptionsServiceComponentName =
@@ -22539,7 +22574,11 @@
 
         @Override
         public @NonNull String[] getKnownPackageNames(int knownPackage, int userId) {
-            switch (knownPackage) {
+            return dropNonSystemPackages(getKnownPackageNamesInternal(knownPackage, userId));
+        }
+
+        private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
+            switch(knownPackage) {
                 case PackageManagerInternal.PACKAGE_BROWSER:
                     return new String[]{mPermissionManager.getDefaultBrowser(userId)};
                 case PackageManagerInternal.PACKAGE_INSTALLER:
@@ -22566,6 +22605,8 @@
                     return filterOnlySystemPackages(mAppPredictionServicePackage);
                 case PackageManagerInternal.PACKAGE_TELEPHONY:
                     return filterOnlySystemPackages(mTelephonyPackages);
+                case PackageManagerInternal.PACKAGE_COMPANION:
+                    return filterOnlySystemPackages("com.android.companiondevicemanager");
                 default:
                     return ArrayUtils.emptyArray(String.class);
             }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6653011..9642a1a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1249,6 +1249,7 @@
             return false;
         }
         ps.clearDomainVerificationStatusForUser(userId);
+        ps.setIntentFilterVerificationInfo(null);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 05545cd..565a85f 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -280,6 +280,9 @@
     public boolean isTelephony() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0;
     }
+    public boolean isCompanion() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7580a30..a6ba77c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3330,6 +3330,13 @@
                 // Special permissions for the system telephony apps.
                 allowed = true;
             }
+            if (!allowed && bp.isCompanion()
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_COMPANION, UserHandle.USER_SYSTEM),
+                    pkg.getPackageName())) {
+                // Special permissions for the system companion device manager.
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index d99e03b..c50248d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -24,7 +24,6 @@
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.telephony.TelephonyManager;
-import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.TimestampedValue;
@@ -32,12 +31,11 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.timezonedetector.ArrayMapWithHistory;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.LinkedList;
-import java.util.Map;
 
 /**
  * An implementation of TimeDetectorStrategy that passes phone and manual suggestions to
@@ -99,14 +97,12 @@
     private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
 
     /**
-     * A mapping from phoneId to a linked list of time suggestions (the "first" being the latest).
-     * We typically expect one or two entries in this Map: devices will have a small number
-     * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
-     * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+     * A mapping from phoneId to a time suggestion. We typically expect one or two mappings: devices
+     * will have a small number of telephony devices and phoneIds are assumed to be stable.
      */
     @GuardedBy("this")
-    private ArrayMap<Integer, LinkedList<PhoneTimeSuggestion>> mSuggestionByPhoneId =
-            new ArrayMap<>();
+    private ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
+            new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     @Override
     public void initialize(@NonNull Callback callback) {
@@ -179,16 +175,7 @@
 
         ipw.println("Phone suggestion history:");
         ipw.increaseIndent(); // level 2
-        for (Map.Entry<Integer, LinkedList<PhoneTimeSuggestion>> entry
-                : mSuggestionByPhoneId.entrySet()) {
-            ipw.println("Phone " + entry.getKey());
-
-            ipw.increaseIndent(); // level 3
-            for (PhoneTimeSuggestion suggestion : entry.getValue()) {
-                ipw.println(suggestion);
-            }
-            ipw.decreaseIndent(); // level 3
-        }
+        mSuggestionByPhoneId.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
         ipw.decreaseIndent(); // level 1
@@ -205,20 +192,10 @@
         }
 
         int phoneId = suggestion.getPhoneId();
-        LinkedList<PhoneTimeSuggestion> phoneSuggestions = mSuggestionByPhoneId.get(phoneId);
-        if (phoneSuggestions == null) {
-            // The first time we've seen this phoneId.
-            phoneSuggestions = new LinkedList<>();
-            mSuggestionByPhoneId.put(phoneId, phoneSuggestions);
-        } else if (phoneSuggestions.isEmpty()) {
-            Slog.w(LOG_TAG, "Suggestions unexpectedly empty when adding suggestion=" + suggestion);
-        }
-
-        if (!phoneSuggestions.isEmpty()) {
+        PhoneTimeSuggestion previousSuggestion = mSuggestionByPhoneId.get(phoneId);
+        if (previousSuggestion != null) {
             // We can log / discard suggestions with obvious issues with the reference time clock.
-            PhoneTimeSuggestion previousSuggestion = phoneSuggestions.getFirst();
-            if (previousSuggestion == null
-                    || previousSuggestion.getUtcTime() == null
+            if (previousSuggestion.getUtcTime() == null
                     || previousSuggestion.getUtcTime().getValue() == null) {
                 // This should be impossible given we only store validated suggestions.
                 Slog.w(LOG_TAG, "Previous suggestion is null or has a null time."
@@ -240,10 +217,7 @@
         }
 
         // Store the latest suggestion.
-        phoneSuggestions.addFirst(suggestion);
-        if (phoneSuggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
-            phoneSuggestions.removeLast();
-        }
+        mSuggestionByPhoneId.put(phoneId, suggestion);
         return true;
     }
 
@@ -331,15 +305,7 @@
         int bestScore = PHONE_INVALID_SCORE;
         for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
             Integer phoneId = mSuggestionByPhoneId.keyAt(i);
-            LinkedList<PhoneTimeSuggestion> phoneSuggestions = mSuggestionByPhoneId.valueAt(i);
-            if (phoneSuggestions == null) {
-                // Unexpected - map is missing a value.
-                Slog.w(LOG_TAG, "Suggestions unexpectedly missing for phoneId."
-                        + " phoneId=" + phoneId);
-                continue;
-            }
-
-            PhoneTimeSuggestion candidateSuggestion = phoneSuggestions.getFirst();
+            PhoneTimeSuggestion candidateSuggestion = mSuggestionByPhoneId.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected - null suggestions should never be stored.
                 Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for phoneId."
@@ -540,10 +506,6 @@
     @VisibleForTesting
     @Nullable
     public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
-        LinkedList<PhoneTimeSuggestion> suggestions = mSuggestionByPhoneId.get(phoneId);
-        if (suggestions == null) {
-            return null;
-        }
-        return suggestions.getFirst();
+        return mSuggestionByPhoneId.get(phoneId);
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
new file mode 100644
index 0000000..3274f0e
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
@@ -0,0 +1,187 @@
+/*
+ * 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.timezonedetector;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * A partial decorator for {@link ArrayMap} that records historic values for each mapping for
+ * debugging later with {@link #dump(IndentingPrintWriter)}.
+ *
+ * <p>This class is only intended for use in {@link TimeZoneDetectorStrategy} and
+ * {@link com.android.server.timedetector.TimeDetectorStrategy} so only provides the parts of the
+ * {@link ArrayMap} API needed. If it is ever extended to include deletion methods like
+ * {@link ArrayMap#remove(Object)} some thought would need to be given to the correct
+ * {@link ArrayMap#containsKey(Object)} behavior for the history. Like {@link ArrayMap}, it is not
+ * thread-safe.
+ *
+ * @param <K> the type of the key
+ * @param <V> the type of the value
+ */
+public final class ArrayMapWithHistory<K, V> {
+    private static final String TAG = "ArrayMapWithHistory";
+
+    /** The size the linked list against each value is allowed to grow to. */
+    private final int mMaxHistorySize;
+
+    @Nullable
+    private ArrayMap<K, ReferenceWithHistory<V>> mMap;
+
+    /**
+     * Creates an instance that records, at most, the specified number of values against each key.
+     */
+    public ArrayMapWithHistory(@IntRange(from = 1) int maxHistorySize) {
+        if (maxHistorySize < 1) {
+            throw new IllegalArgumentException("maxHistorySize < 1: " + maxHistorySize);
+        }
+        mMaxHistorySize = maxHistorySize;
+    }
+
+    /**
+     * See {@link ArrayMap#put(K, V)}.
+     */
+    @Nullable
+    public V put(@Nullable K key, @Nullable V value) {
+        if (mMap == null) {
+            mMap = new ArrayMap<>();
+        }
+
+        ReferenceWithHistory<V> valueHolder = mMap.get(key);
+        if (valueHolder == null) {
+            valueHolder = new ReferenceWithHistory<>(mMaxHistorySize);
+            mMap.put(key, valueHolder);
+        } else if (valueHolder.getHistoryCount() == 0) {
+            Log.w(TAG, "History for \"" + key + "\" was unexpectedly empty");
+        }
+
+        return valueHolder.set(value);
+    }
+
+    /**
+     * See {@link ArrayMap#get(Object)}.
+     */
+    @Nullable
+    public V get(@Nullable Object key) {
+        if (mMap == null) {
+            return null;
+        }
+
+        ReferenceWithHistory<V> valueHolder = mMap.get(key);
+        if (valueHolder == null) {
+            return null;
+        } else if (valueHolder.getHistoryCount() == 0) {
+            Log.w(TAG, "History for \"" + key + "\" was unexpectedly empty");
+        }
+        return valueHolder.get();
+    }
+
+    /**
+     * See {@link ArrayMap#size()}.
+     */
+    public int size() {
+        return mMap == null ? 0 : mMap.size();
+    }
+
+    /**
+     * See {@link ArrayMap#keyAt(int)}.
+     */
+    @Nullable
+    public K keyAt(int index) {
+        if (mMap == null) {
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
+        return mMap.keyAt(index);
+    }
+
+    /**
+     * See {@link ArrayMap#valueAt(int)}.
+     */
+    @Nullable
+    public V valueAt(int index) {
+        if (mMap == null) {
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
+
+        ReferenceWithHistory<V> valueHolder = mMap.valueAt(index);
+        if (valueHolder == null || valueHolder.getHistoryCount() == 0) {
+            Log.w(TAG, "valueAt(" + index + ") was unexpectedly null or empty");
+            return null;
+        }
+        return valueHolder.get();
+    }
+
+    /**
+     * Dumps the content of the map, including historic values, using the supplied writer.
+     */
+    public void dump(@NonNull IndentingPrintWriter ipw) {
+        if (mMap == null) {
+            ipw.println("{Empty}");
+        } else {
+            for (int i = 0; i < mMap.size(); i++) {
+                ipw.println("key idx: " + i + "=" + mMap.keyAt(i));
+                ReferenceWithHistory<V> value = mMap.valueAt(i);
+                ipw.println("val idx: " + i + "=" +  value);
+                ipw.increaseIndent();
+
+                ipw.println("Historic values=[");
+                ipw.increaseIndent();
+                value.dump(ipw);
+                ipw.decreaseIndent();
+                ipw.println("]");
+
+                ipw.decreaseIndent();
+            }
+        }
+        ipw.flush();
+    }
+
+    /**
+     * Internal method intended for tests that returns the number of historic values associated with
+     * the supplied key currently. If there is no mapping for the key then {@code 0} is returned.
+     */
+    @VisibleForTesting
+    public int getHistoryCountForKeyForTests(@Nullable K key) {
+        if (mMap == null) {
+            return 0;
+        }
+
+        ReferenceWithHistory<V> valueHolder = mMap.get(key);
+        if (valueHolder == null) {
+            return 0;
+        } else if (valueHolder.getHistoryCount() == 0) {
+            Log.w(TAG, "getValuesSizeForKeyForTests(\"" + key + "\") was unexpectedly empty");
+            return 0;
+        } else {
+            return valueHolder.getHistoryCount();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ArrayMapWithHistory{"
+                + "mHistorySize=" + mMaxHistorySize
+                + ", mMap=" + mMap
+                + '}';
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
new file mode 100644
index 0000000..8bd1035
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.LinkedList;
+
+/**
+ * A class that behaves like the following definition, except it stores the history of values set
+ * that can be dumped for debugging with {@link #dump(IndentingPrintWriter)}.
+ *
+ * <pre>{@code
+ *     private static class Ref<V> {
+ *         private V mValue;
+ *
+ *         public V get() {
+ *             return mValue;
+ *         }
+ *
+ *         public V set(V value) {
+ *             V previous = mValue;
+ *             mValue = value;
+ *             return previous;
+ *         }
+ *     }
+ * }</pre>
+ *
+ * <p>This class is not thread-safe.
+ *
+ * @param <V> the type of the value
+ */
+public final class ReferenceWithHistory<V> {
+
+    /** The size the history linked list is allowed to grow to. */
+    private final int mMaxHistorySize;
+
+    @Nullable
+    private LinkedList<V> mValues;
+
+    /**
+     * Creates an instance that records, at most, the specified number of values.
+     */
+    public ReferenceWithHistory(@IntRange(from = 1) int maxHistorySize) {
+        if (maxHistorySize < 1) {
+            throw new IllegalArgumentException("maxHistorySize < 1: " + maxHistorySize);
+        }
+        this.mMaxHistorySize = maxHistorySize;
+    }
+
+    /** Returns the current value, or {@code null} if it has never been set. */
+    @Nullable
+    public V get() {
+        return (mValues == null || mValues.isEmpty()) ? null : mValues.getFirst();
+    }
+
+    /** Sets the current value. Returns the previous value, or {@code null}. */
+    @Nullable
+    public V set(@Nullable V newValue) {
+        if (mValues == null) {
+            mValues = new LinkedList<>();
+        }
+
+        V previous = get();
+
+        mValues.addFirst(newValue);
+        if (mValues.size() > mMaxHistorySize) {
+            mValues.removeLast();
+        }
+        return previous;
+    }
+
+    /**
+     * Dumps the content of the reference, including historic values, using the supplied writer.
+     */
+    public void dump(@NonNull IndentingPrintWriter ipw) {
+        if (mValues == null) {
+            ipw.println("{Empty}");
+        } else {
+            int i = 0;
+            for (V value : mValues) {
+                ipw.println(i + ": " + value);
+                i++;
+            }
+        }
+        ipw.flush();
+    }
+
+    /**
+     * Returns the number of historic entries stored currently.
+     */
+    public int getHistoryCount() {
+        return mValues == null ? 0 : mValues.size();
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(get());
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index b3013c7..b4a4399 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -27,7 +27,6 @@
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Slog;
 
@@ -38,8 +37,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.LinkedList;
-import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -175,14 +172,13 @@
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
 
     /**
-     * A mapping from phoneId to a linked list of phone time zone suggestions (the head being the
-     * latest). We typically expect one or two entries in this Map: devices will have a small number
-     * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
-     * the ID will not exceed {@link #KEEP_PHONE_SUGGESTION_HISTORY_SIZE} in size.
+     * A mapping from phoneId to a phone time zone suggestion. We typically expect one or two
+     * mappings: devices will have a small number of telephony devices and phoneIds are assumed to
+     * be stable.
      */
     @GuardedBy("this")
-    private ArrayMap<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> mSuggestionByPhoneId =
-            new ArrayMap<>();
+    private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionByPhoneId =
+            new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
 
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategy}.
@@ -226,16 +222,7 @@
                 new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
 
         // Store the suggestion against the correct phoneId.
-        LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
-                mSuggestionByPhoneId.get(suggestion.getPhoneId());
-        if (suggestions == null) {
-            suggestions = new LinkedList<>();
-            mSuggestionByPhoneId.put(suggestion.getPhoneId(), suggestions);
-        }
-        suggestions.addFirst(scoredSuggestion);
-        if (suggestions.size() > KEEP_PHONE_SUGGESTION_HISTORY_SIZE) {
-            suggestions.removeLast();
-        }
+        mSuggestionByPhoneId.put(suggestion.getPhoneId(), scoredSuggestion);
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
@@ -398,13 +385,7 @@
         // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
         // expected to withdraw suggestions they no longer have confidence in.
         for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
-            LinkedList<QualifiedPhoneTimeZoneSuggestion> phoneSuggestions =
-                    mSuggestionByPhoneId.valueAt(i);
-            if (phoneSuggestions == null) {
-                // Unexpected
-                continue;
-            }
-            QualifiedPhoneTimeZoneSuggestion candidateSuggestion = phoneSuggestions.getFirst();
+            QualifiedPhoneTimeZoneSuggestion candidateSuggestion = mSuggestionByPhoneId.valueAt(i);
             if (candidateSuggestion == null) {
                 // Unexpected
                 continue;
@@ -474,16 +455,7 @@
 
         ipw.println("Phone suggestion history:");
         ipw.increaseIndent(); // level 2
-        for (Map.Entry<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> entry
-                : mSuggestionByPhoneId.entrySet()) {
-            ipw.println("Phone " + entry.getKey());
-
-            ipw.increaseIndent(); // level 3
-            for (QualifiedPhoneTimeZoneSuggestion suggestion : entry.getValue()) {
-                ipw.println(suggestion);
-            }
-            ipw.decreaseIndent(); // level 3
-        }
+        mSuggestionByPhoneId.dump(ipw);
         ipw.decreaseIndent(); // level 2
         ipw.decreaseIndent(); // level 1
         ipw.flush();
@@ -494,12 +466,7 @@
      */
     @VisibleForTesting
     public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
-        LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
-                mSuggestionByPhoneId.get(phoneId);
-        if (suggestions == null) {
-            return null;
-        }
-        return suggestions.getFirst();
+        return mSuggestionByPhoneId.get(phoneId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 674955e..6ad439e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -51,7 +51,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
@@ -1688,19 +1687,9 @@
             return START_SUCCESS;
         }
 
-        // True if we are clearing top and resetting of a standard (default) launch mode
-        // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
-        final boolean clearTopAndResetStandardLaunchMode =
-                (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
-                        == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
-                        && mLaunchMode == LAUNCH_MULTIPLE;
-
         boolean clearTaskForReuse = false;
         if (reusedTask != null) {
-            // If mStartActivity does not have a task associated with it, associate it with the
-            // reused activity's task. Do not do so if we're clearing top and resetting for a
-            // standard launchMode activity.
-            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
+            if (mStartActivity.getTask() == null) {
                 mStartActivity.setTaskForReuse(reusedTask);
                 clearTaskForReuse = true;
             }
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 1e27007..f0758dd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -44,7 +44,6 @@
 import android.app.IActivityManager;
 import android.app.IUidObserver;
 import android.app.usage.UsageStatsManager;
-import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -82,6 +81,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Random;
@@ -690,11 +690,11 @@
         entries.add(new OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
                 AppOpsManager.MODE_IGNORED,
-                new Pair[0]));
+                Collections.emptyMap()));
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED,
-                new Pair[0]));
+                Collections.emptyMap()));
 
         ops.add(new PackageOps(PACKAGE_1, UID_1, entries));
 
@@ -703,7 +703,7 @@
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED,
-                new Pair[0]));
+                Collections.emptyMap()));
 
         ops.add(new PackageOps(PACKAGE_2, UID_2, entries));
 
@@ -712,7 +712,7 @@
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_ALLOWED,
-                new Pair[0]));
+                Collections.emptyMap()));
 
         ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries));
 
@@ -721,11 +721,11 @@
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED,
-                new Pair[0]));
+                Collections.emptyMap()));
         entries.add(new OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
                 AppOpsManager.MODE_IGNORED,
-                new Pair[0]));
+                Collections.emptyMap()));
 
         ops.add(new PackageOps(PACKAGE_3, UID_10_3, entries));
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index fa209a7a..529339e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
+import static android.app.AppOpsManager.OP_FLAGS_ALL;
 import static android.app.AppOpsManager.OP_READ_SMS;
 import static android.app.AppOpsManager.OP_WIFI_SCAN;
 import static android.app.AppOpsManager.OP_WRITE_SMS;
@@ -465,11 +466,11 @@
                 assertWithMessage("Unexpected mode").that(mode).isEqualTo(opEntry.getMode());
                 if (minMillis > 0) {
                     assertWithMessage("Unexpected timestamp")
-                            .that(opEntry.getTime()).isAtLeast(minMillis);
+                            .that(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isAtLeast(minMillis);
                 }
                 if (minRejectMillis > 0) {
-                    assertWithMessage("Unexpected rejection timestamp")
-                            .that(opEntry.getRejectTime()).isAtLeast(minRejectMillis);
+                    assertWithMessage("Unexpected rejection timestamp").that(
+                            opEntry.getLastRejectTime(OP_FLAGS_ALL)).isAtLeast(minRejectMillis);
                 }
             }
         }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
new file mode 100644
index 0000000..b6eea46
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.util.ArrayMap;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.timezonedetector.ArrayMapWithHistory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.StringWriter;
+import java.util.concurrent.Callable;
+
+@RunWith(AndroidJUnit4.class)
+public class ArrayMapWithHistoryTest {
+
+    @Test
+    public void testValueHistoryBehavior() {
+        // Create a map that will retain 2 values per key.
+        ArrayMapWithHistory<String, String> historyMap = new ArrayMapWithHistory<>(2 /* history */);
+        ArrayMap<String, String> arrayMap = new ArrayMap<>();
+
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", null));
+
+        assertEquals(0, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertToStringAndDumpNotNull(historyMap);
+
+        putAndCompareReturnValue(historyMap, arrayMap, "K1", "V1");
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", "V1"));
+        compareKeyAtAndValueAtForIndex(0, historyMap, arrayMap);
+
+        assertEquals(1, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertToStringAndDumpNotNull(historyMap);
+
+        // put() a new value for the same key.
+        putAndCompareReturnValue(historyMap, arrayMap, "K1", "V2");
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", "V2"));
+        compareKeyAtAndValueAtForIndex(0, historyMap, arrayMap);
+
+        assertEquals(2, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertToStringAndDumpNotNull(historyMap);
+
+        // put() a new value for the same key. We should have hit the limit of "2 values retained
+        // per key".
+        putAndCompareReturnValue(historyMap, arrayMap, "K1", "V3");
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", "V3"));
+        compareKeyAtAndValueAtForIndex(0, historyMap, arrayMap);
+
+        assertEquals(2, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertToStringAndDumpNotNull(historyMap);
+    }
+
+    @Test
+    public void testMapBehavior() throws Exception {
+        ArrayMapWithHistory<String, String> historyMap = new ArrayMapWithHistory<>(2);
+        ArrayMap<String, String> arrayMap = new ArrayMap<>();
+
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", null), entry("K2", null));
+        assertIndexAccessThrowsException(0, historyMap, arrayMap);
+
+        assertEquals(0, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertEquals(0, historyMap.getHistoryCountForKeyForTests("K2"));
+
+        putAndCompareReturnValue(historyMap, arrayMap, "K1", "V1");
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", "V1"), entry("K2", null));
+        compareKeyAtAndValueAtForIndex(0, historyMap, arrayMap);
+        // TODO Restore after http://b/146563025 is fixed and ArrayMap behaves properly in tests.
+        // assertIndexAccessThrowsException(1, historyMap, arrayMap);
+
+        assertEquals(1, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertToStringAndDumpNotNull(historyMap);
+
+        putAndCompareReturnValue(historyMap, arrayMap, "K2", "V2");
+        compareGetAndSizeForKeys(historyMap, arrayMap, entry("K1", "V1"), entry("K2", "V2"));
+        compareKeyAtAndValueAtForIndex(0, historyMap, arrayMap);
+        compareKeyAtAndValueAtForIndex(1, historyMap, arrayMap);
+        // TODO Restore after http://b/146563025 is fixed and ArrayMap behaves properly in tests.
+        // assertIndexAccessThrowsException(2, historyMap, arrayMap);
+
+        assertEquals(1, historyMap.getHistoryCountForKeyForTests("K1"));
+        assertEquals(1, historyMap.getHistoryCountForKeyForTests("K2"));
+        assertToStringAndDumpNotNull(historyMap);
+    }
+
+    private static String dumpHistoryMap(ArrayMapWithHistory<?, ?> historyMap) {
+        StringWriter stringWriter = new StringWriter();
+        try (IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ")) {
+            historyMap.dump(ipw);
+            return stringWriter.toString();
+        }
+    }
+
+    private static <K, V> void putAndCompareReturnValue(ArrayMapWithHistory<K, V> historyMap,
+            ArrayMap<K, V> arrayMap, K key, V value) {
+        assertEquals(arrayMap.put(key, value), historyMap.put(key, value));
+    }
+
+    private static class Entry<K, V> {
+        public final K key;
+        public final V value;
+
+        Entry(K key, V value) {
+            this.key = key;
+            this.value = value;
+        }
+    }
+
+    private static <K, V> Entry<K, V> entry(K key, V value) {
+        return new Entry<>(key, value);
+    }
+
+    @SafeVarargs
+    private static <K, V> void compareGetAndSizeForKeys(ArrayMapWithHistory<K, V> historyMap,
+            ArrayMap<K, V> arrayMap, Entry<K, V>... expectedEntries) {
+        for (Entry<K, V> expectedEntry : expectedEntries) {
+            assertEquals(arrayMap.get(expectedEntry.key), historyMap.get(expectedEntry.key));
+            assertEquals(expectedEntry.value, historyMap.get(expectedEntry.key));
+        }
+        assertEquals(arrayMap.size(), historyMap.size());
+    }
+
+    private static void compareKeyAtAndValueAtForIndex(
+            int index, ArrayMapWithHistory<?, ?> historyMap, ArrayMap<?, ?> arrayMap) {
+        assertEquals(arrayMap.keyAt(index), historyMap.keyAt(index));
+        assertEquals(arrayMap.valueAt(index), historyMap.valueAt(index));
+    }
+
+    private static void assertIndexAccessThrowsException(
+            int index, ArrayMapWithHistory<?, ?> historyMap, ArrayMap<?, ?> arrayMap)
+            throws Exception {
+        assertThrowsArrayIndexOutOfBoundsException(
+                "ArrayMap.keyAt(" + index + ")", () -> arrayMap.keyAt(index));
+        assertThrowsArrayIndexOutOfBoundsException(
+                "ArrayMapWithHistory.keyAt(" + index + ")", () -> historyMap.keyAt(index));
+        assertThrowsArrayIndexOutOfBoundsException(
+                "ArrayMap.keyAt(" + index + ")", () -> arrayMap.valueAt(index));
+        assertThrowsArrayIndexOutOfBoundsException(
+                "ArrayMapWithHistory.keyAt(" + index + ")", () -> historyMap.valueAt(index));
+    }
+
+    private static void assertThrowsArrayIndexOutOfBoundsException(
+            String description, Callable<?> callable) throws Exception {
+        try {
+            callable.call();
+            fail("Expected exception for " + description);
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            // This is fine.
+        } catch (Exception e) {
+            // Any other exception is just rethrown.
+            throw e;
+        }
+    }
+
+    private static void assertToStringAndDumpNotNull(ArrayMapWithHistory<?, ?> historyMap) {
+        assertNotNull(historyMap.toString());
+        assertNotNull(dumpHistoryMap(historyMap));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
new file mode 100644
index 0000000..ce72499
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.timezonedetector.ReferenceWithHistory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.StringWriter;
+
+@RunWith(AndroidJUnit4.class)
+public class ReferenceWithHistoryTest {
+
+    @Test
+    public void testBasicReferenceBehavior() {
+        // Create a reference that will retain 2 history values.
+        ReferenceWithHistory<String> referenceWithHistory =
+                new ReferenceWithHistory<>(2 /* history */);
+        TestRef<String> reference = new TestRef<>();
+
+        // Check unset behavior.
+        compareGet(referenceWithHistory, reference, null);
+        assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+        compareToString(referenceWithHistory, reference, "null");
+
+        // Try setting null.
+        setAndCompareReturnValue(referenceWithHistory, reference, null);
+        compareGet(referenceWithHistory, reference, null);
+        assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+        compareToString(referenceWithHistory, reference, "null");
+
+        // Try setting a non-null value.
+        setAndCompareReturnValue(referenceWithHistory, reference, "Foo");
+        compareGet(referenceWithHistory, reference, "Foo");
+        assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+        compareToString(referenceWithHistory, reference, "Foo");
+
+        // Try setting null again.
+        setAndCompareReturnValue(referenceWithHistory, reference, "Foo");
+        compareGet(referenceWithHistory, reference, "Foo");
+        assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+        compareToString(referenceWithHistory, reference, "Foo");
+
+        // Try a non-null value again.
+        setAndCompareReturnValue(referenceWithHistory, reference, "Bar");
+        compareGet(referenceWithHistory, reference, "Bar");
+        assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+        compareToString(referenceWithHistory, reference, "Bar");
+    }
+
+    @Test
+    public void testValueHistoryBehavior() {
+        // Create a reference that will retain 2 history values.
+        ReferenceWithHistory<String> referenceWithHistory =
+                new ReferenceWithHistory<>(2 /* history */);
+        TestRef<String> reference = new TestRef<>();
+
+        // Assert behavior before anything is set.
+        assertEquals(0, referenceWithHistory.getHistoryCount());
+
+        // Set a value (1).
+        setAndCompareReturnValue(referenceWithHistory, reference, "V1");
+        assertEquals(1, referenceWithHistory.getHistoryCount());
+
+        // Set a value (2).
+        setAndCompareReturnValue(referenceWithHistory, reference, "V2");
+        assertEquals(2, referenceWithHistory.getHistoryCount());
+
+        // Set a value (3).
+        // We should have hit the limit of "2 history values retained per key".
+        setAndCompareReturnValue(referenceWithHistory, reference, "V3");
+        assertEquals(2, referenceWithHistory.getHistoryCount());
+    }
+
+    /**
+     * A simple class that has the same behavior as ReferenceWithHistory without the history. Used
+     * in tests for comparison.
+     */
+    private static class TestRef<V> {
+        private V mValue;
+
+        public V get() {
+            return mValue;
+        }
+
+        public V set(V value) {
+            V previous = mValue;
+            mValue = value;
+            return previous;
+        }
+
+        public String toString() {
+            return String.valueOf(mValue);
+        }
+    }
+
+    private static void compareGet(
+            ReferenceWithHistory<?> referenceWithHistory, TestRef<?> reference, Object value) {
+        assertEquals(reference.get(), referenceWithHistory.get());
+        assertEquals(value, reference.get());
+    }
+
+    private static <T> void setAndCompareReturnValue(
+            ReferenceWithHistory<T> referenceWithHistory, TestRef<T> reference, T newValue) {
+        assertEquals(reference.set(newValue), referenceWithHistory.set(newValue));
+    }
+
+    private static void compareToString(
+            ReferenceWithHistory<?> referenceWithHistory, TestRef<?> reference, String expected) {
+        assertEquals(reference.toString(), referenceWithHistory.toString());
+        assertEquals(expected, referenceWithHistory.toString());
+    }
+
+    private static String dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) {
+        StringWriter stringWriter = new StringWriter();
+        try (IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ")) {
+            referenceWithHistory.dump(ipw);
+            return stringWriter.toString();
+        }
+    }
+}
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
index 367aad1..06c08f5 100644
--- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -20,8 +20,8 @@
 import android.database.Cursor;
 import android.database.SQLException;
 import android.os.Binder;
-import android.os.Build;
 import android.os.PersistableBundle;
+import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
@@ -43,7 +43,7 @@
  */
 public class SmsNumberUtils {
     private static final String TAG = "SmsNumberUtils";
-    private static final boolean DBG = Build.IS_DEBUGGABLE;
+    private static final boolean DBG = SystemProperties.getInt("ro.debuggable", 0) == 1;
 
     private static final String PLUS_SIGN = "+";
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7381acb..ed1bff7 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3048,16 +3048,18 @@
             "data_switch_validation_timeout_long";
 
     /**
-     * GPS configs. See android.hardware.gnss@1.0 IGnssConfiguration.
-     * @hide
+     * GPS configs. See the GNSS HAL documentation for more details.
      */
     public static final class Gps {
+        private Gps() {}
+
         /** Prefix of all Gps.KEY_* constants. */
         public static final String KEY_PREFIX = "gps.";
 
         /**
          * Location information during (and after) an emergency call is only provided over control
          * plane signaling from the network.
+         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
 
@@ -3065,6 +3067,7 @@
          * Location information during (and after) an emergency call is provided over the data
          * plane and serviced by the framework GNSS service, but if it fails, the carrier also
          * supports control plane backup signaling.
+         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
 
@@ -3072,6 +3075,7 @@
          * Location information during (and after) an emergency call is provided over the data plane
          * and serviced by the framework GNSS service only. There is no backup signalling over the
          * control plane if it fails.
+         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
 
@@ -3088,10 +3092,14 @@
         /**
          * SUPL server host for SET Initiated & non-ES Network-Initiated SUPL requests.
          * Default to supl.google.com
+         * @hide
          */
         public static final String KEY_SUPL_HOST_STRING = KEY_PREFIX + "supl_host";
 
-        /** SUPL server port. Default to 7275. */
+        /**
+         * SUPL server port. Default to 7275.
+         * @hide
+         */
         public static final String KEY_SUPL_PORT_STRING = KEY_PREFIX + "supl_port";
 
         /**
@@ -3099,6 +3107,7 @@
          * with bits 0:7 representing a service indicator field, bits 8:15
          * representing the minor version and bits 16:23 representing the
          * major version. Default to 0x20000.
+         * @hide
          */
         public static final String KEY_SUPL_VER_STRING = KEY_PREFIX + "supl_ver";
 
@@ -3106,6 +3115,7 @@
          * SUPL_MODE configuration bit mask
          * 1 - Mobile Station Based. This is default.
          * 2 - Mobile Station Assisted.
+         * @hide
          */
         public static final String KEY_SUPL_MODE_STRING = KEY_PREFIX + "supl_mode";
 
@@ -3114,6 +3124,7 @@
          * (e.g. E911), and SUPL non-ES requests to only outside of non user emergency sessions.
          * 0 - no.
          * 1 - yes. This is default.
+         * @hide
          */
         public static final String KEY_SUPL_ES_STRING = KEY_PREFIX + "supl_es";
 
@@ -3122,6 +3133,7 @@
          * 0 - Radio Resource Location Protocol in user plane and control plane. This is default.
          * 1 - Enable LTE Positioning Protocol in user plane.
          * 2 - Enable LTE Positioning Protocol in control plane.
+         * @hide
          */
         public static final String KEY_LPP_PROFILE_STRING = KEY_PREFIX + "lpp_profile";
 
@@ -3129,6 +3141,7 @@
          * Determine whether to use emergency PDN for emergency SUPL.
          * 0 - no.
          * 1 - yes. This is default.
+         * @hide
          */
         public static final String KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING =
                 KEY_PREFIX + "use_emergency_pdn_for_emergency_supl";
@@ -3139,6 +3152,7 @@
          * 1 - Use A-GLONASS in Radio Resource Control(RRC) control-plane.
          * 2 - Use A-GLONASS in Radio Resource Location user-plane.
          * 4 - Use A-GLONASS in LTE Positioning Protocol User plane.
+         * @hide
          */
         public static final String KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING =
                 KEY_PREFIX + "a_glonass_pos_protocol_select";
@@ -3150,11 +3164,13 @@
          * "1" - Lock Mobile Originated GPS functionalities.
          * "2" - Lock Network initiated GPS functionalities.
          * "3" - Lock both. This is default.
+         * @hide
          */
         public static final String KEY_GPS_LOCK_STRING = KEY_PREFIX + "gps_lock";
 
         /**
          * Control Plane / SUPL NI emergency extension time in seconds. Default to "0".
+         * @hide
          */
         public static final String KEY_ES_EXTENSION_SEC_STRING = KEY_PREFIX + "es_extension_sec";
 
@@ -3163,6 +3179,7 @@
          * the non-framework entities requesting location directly from GNSS without involving
          * the framework, as managed by IGnssVisibilityControl.hal. For example,
          * "com.example.mdt com.example.ims".
+         * @hide
          */
         public static final String KEY_NFW_PROXY_APPS_STRING = KEY_PREFIX + "nfw_proxy_apps";
 
@@ -3178,6 +3195,7 @@
          * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
          * <p>
          * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+         * @hide
          */
         public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
                 + "es_supl_control_plane_support_int";
@@ -3189,6 +3207,7 @@
          * <p>
          * A string array of PLMNs that do not support a control-plane mechanism for getting a
          * user's location for SUPL ES.
+         * @hide
          */
         public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
                 KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java
index 7314edd..84be4e8 100644
--- a/telephony/java/android/telephony/CbGeoUtils.java
+++ b/telephony/java/android/telephony/CbGeoUtils.java
@@ -26,7 +26,6 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-
 /**
  * This utils class is used for geo-fencing of CellBroadcast messages and is used by the cell
  * broadcast module.
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index 407ad19..0d2a8bc 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -23,6 +23,7 @@
 import android.os.TelephonyServiceManager;
 import android.telephony.euicc.EuiccCardManager;
 import android.telephony.euicc.EuiccManager;
+import android.telephony.ims.ImsManager;
 
 import com.android.internal.util.Preconditions;
 
@@ -88,6 +89,11 @@
                 EuiccCardManager.class,
                 context -> new EuiccCardManager(context)
         );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.TELEPHONY_IMS_SERVICE,
+                ImsManager.class,
+                context -> new ImsManager(context)
+        );
     }
 
     /** @hide */
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index dcea9bb..d672642 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -18,11 +18,11 @@
 
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
-import android.os.Build;
 import android.telephony.Rlog;
 import android.util.SparseIntArray;
 
 import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.telephony.util.XmlUtils;
 
 import dalvik.annotation.compat.UnsupportedAppUsage;
@@ -30,7 +30,7 @@
 public class Sms7BitEncodingTranslator {
     private static final String TAG = "Sms7BitEncodingTranslator";
     @UnsupportedAppUsage
-    private static final boolean DBG = Build.IS_DEBUGGABLE ;
+    private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
     private static boolean mIs7BitTranslationTableLoaded = false;
     private static SparseIntArray mTranslationTable = null;
     @UnsupportedAppUsage
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 08115ec..fb1f866 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -32,7 +32,6 @@
         // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
         // to a separate package.
         "java/android/net/wifi/WifiNetworkScoreCache.java",
-        "java/android/net/wifi/WifiCondManager.java",
         "java/android/net/wifi/wificond/*.java",
         ":libwificond_ipc_aidl",
     ],
diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java
index 375a977..24ed8ef 100644
--- a/wifi/java/android/net/wifi/SoftApInfo.java
+++ b/wifi/java/android/net/wifi/SoftApInfo.java
@@ -16,15 +16,12 @@
 
 package android.net.wifi;
 
-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;
 import java.util.Objects;
 
 /**
@@ -85,26 +82,12 @@
      */
     public static final int CHANNEL_WIDTH_160MHZ = 6;
 
-    /**
-     * @hide
-     */
-    @IntDef(prefix = { "CHANNEL_WIDTH_" }, value = {
-            CHANNEL_WIDTH_INVALID,
-            CHANNEL_WIDTH_20MHZ_NOHT,
-            CHANNEL_WIDTH_20MHZ,
-            CHANNEL_WIDTH_40MHZ,
-            CHANNEL_WIDTH_80MHZ,
-            CHANNEL_WIDTH_80MHZ_PLUS_MHZ,
-            CHANNEL_WIDTH_160MHZ,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Bandwidth {}
 
 
     /** The frequency which AP resides on.  */
     private int mFrequency = 0;
 
-    @Bandwidth
+    @WifiAnnotations.Bandwidth
     private int mBandwidth = CHANNEL_WIDTH_INVALID;
 
     /**
@@ -127,9 +110,9 @@
      *
      * @return One of {@link #CHANNEL_WIDTH_20MHZ}, {@link #CHANNEL_WIDTH_40MHZ},
      * {@link #CHANNEL_WIDTH_80MHZ}, {@link #CHANNEL_WIDTH_160MHZ},
-     * {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ} or {@link #CHANNEL_WIDTH_UNKNOWN}.
+     * {@link #CHANNEL_WIDTH_80MHZ_PLUS_MHZ} or {@link #CHANNEL_WIDTH_INVALID}.
      */
-    @Bandwidth
+    @WifiAnnotations.Bandwidth
     public int getBandwidth() {
         return mBandwidth;
     }
@@ -138,7 +121,7 @@
      * Set AP Channel bandwidth.
      * @hide
      */
-    public void setBandwidth(@Bandwidth int bandwidth) {
+    public void setBandwidth(@WifiAnnotations.Bandwidth int bandwidth) {
         mBandwidth = bandwidth;
     }
 
diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java
index 4a7dee1..9223d28 100644
--- a/wifi/java/android/net/wifi/WifiAnnotations.java
+++ b/wifi/java/android/net/wifi/WifiAnnotations.java
@@ -48,4 +48,16 @@
             WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY,
             WifiScanner.WIFI_BAND_6_GHZ})
     public @interface WifiBandBasic {}
+
+    @IntDef(prefix = { "CHANNEL_WIDTH_" }, value = {
+            SoftApInfo.CHANNEL_WIDTH_INVALID,
+            SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT,
+            SoftApInfo.CHANNEL_WIDTH_20MHZ,
+            SoftApInfo.CHANNEL_WIDTH_40MHZ,
+            SoftApInfo.CHANNEL_WIDTH_80MHZ,
+            SoftApInfo.CHANNEL_WIDTH_80MHZ_PLUS_MHZ,
+            SoftApInfo.CHANNEL_WIDTH_160MHZ,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Bandwidth {}
 }
diff --git a/wifi/java/android/net/wifi/aware/Characteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java
index e2cf4dc..d5fd48e 100644
--- a/wifi/java/android/net/wifi/aware/Characteristics.java
+++ b/wifi/java/android/net/wifi/aware/Characteristics.java
@@ -16,10 +16,14 @@
 
 package android.net.wifi.aware;
 
+import android.annotation.IntDef;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The characteristics of the Wi-Fi Aware implementation.
  */
@@ -31,6 +35,8 @@
             "key_max_service_specific_info_length";
     /** @hide */
     public static final String KEY_MAX_MATCH_FILTER_LENGTH = "key_max_match_filter_length";
+    /** @hide */
+    public static final String KEY_SUPPORTED_CIPHER_SUITES = "key_supported_cipher_suites";
 
     private Bundle mCharacteristics = new Bundle();
 
@@ -71,12 +77,41 @@
      * {@link PublishConfig.Builder#setMatchFilter(java.util.List)} and
      * {@link SubscribeConfig.Builder#setMatchFilter(java.util.List)}.
      *
-     * @return A positive integer, maximum legngth of byte array for Aware discovery match filter.
+     * @return A positive integer, maximum length of byte array for Aware discovery match filter.
      */
     public int getMaxMatchFilterLength() {
         return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH);
     }
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "WIFI_AWARE_CIPHER_SUITE_" }, value = {
+            WIFI_AWARE_CIPHER_SUITE_NCS_SK_128,
+            WIFI_AWARE_CIPHER_SUITE_NCS_SK_256,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiAwareCipherSuites {}
+
+    /**
+     * Wi-Fi Aware supported ciphier suite representing NCS SK 128: 128 bit shared-key.
+     */
+    public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1 << 0;
+
+    /**
+     * Wi-Fi Aware supported ciphier suite representing NCS SK 256: 256 bit shared-key.
+     */
+    public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_256 = 1 << 1;
+
+    /**
+     * Returns the set of cipher suites supported by the device for use in Wi-Fi Aware data-paths.
+     * The device automatically picks the strongest cipher suite when initiating a data-path setup.
+     *
+     * @return A set of flags from {@link #WIFI_AWARE_CIPHER_SUITE_NCS_SK_128}, or
+     * {@link #WIFI_AWARE_CIPHER_SUITE_NCS_SK_256}.
+     */
+    public @WifiAwareCipherSuites int getSupportedCipherSuites() {
+        return mCharacteristics.getInt(KEY_SUPPORTED_CIPHER_SUITES);
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeBundle(mCharacteristics);
diff --git a/wifi/java/android/net/wifi/wificond/NativeScanResult.java b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
index ff8e935..6ed1708 100644
--- a/wifi/java/android/net/wifi/wificond/NativeScanResult.java
+++ b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
@@ -16,46 +16,163 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.List;
 
 /**
- * ScanResult from wificond
+ * Raw scan result data from the wificond daemon.
  *
  * @hide
  */
-public class NativeScanResult implements Parcelable {
+@SystemApi
+public final class NativeScanResult implements Parcelable {
     private static final int CAPABILITY_SIZE = 16;
 
+    /** @hide */
+    @VisibleForTesting
     public byte[] ssid;
+    /** @hide */
+    @VisibleForTesting
     public byte[] bssid;
+    /** @hide */
+    @VisibleForTesting
     public byte[] infoElement;
+    /** @hide */
+    @VisibleForTesting
     public int frequency;
+    /** @hide */
+    @VisibleForTesting
     public int signalMbm;
+    /** @hide */
+    @VisibleForTesting
     public long tsf;
+    /** @hide */
+    @VisibleForTesting
     public BitSet capability;
+    /** @hide */
+    @VisibleForTesting
     public boolean associated;
+    /** @hide */
+    @VisibleForTesting
     public List<RadioChainInfo> radioChainInfos;
 
-    /** public constructor */
-    public NativeScanResult() { }
-
-    /** copy constructor */
-    public NativeScanResult(NativeScanResult source) {
-        ssid = source.ssid.clone();
-        bssid = source.bssid.clone();
-        infoElement = source.infoElement.clone();
-        frequency = source.frequency;
-        signalMbm = source.signalMbm;
-        tsf = source.tsf;
-        capability = (BitSet) source.capability.clone();
-        associated = source.associated;
+    /**
+     * Returns the SSID raw byte array of the AP represented by this scan result.
+     *
+     * @return A byte array.
+     */
+    @NonNull public byte[] getSsid() {
+        return ssid;
     }
 
+    /**
+     * Returns raw bytes representing the MAC address (BSSID) of the AP represented by this scan
+     * result.
+     *
+     * @return a byte array, possibly null or containing the incorrect number of bytes for a MAC
+     * address.
+     */
+    @NonNull public byte[] getBssid() {
+        return bssid;
+    }
+
+    /**
+     * Returns the raw bytes of the information element advertised by the AP represented by this
+     * scan result.
+     *
+     * @return A byte array, possibly null or containing an invalid TLV configuration.
+     */
+    @NonNull public byte[] getInformationElements() {
+        return infoElement;
+    }
+
+    /**
+     * Returns the frequency (in MHz) on which the AP represented by this scan result was observed.
+     *
+     * @return The frequency in MHz.
+     */
+    public int getFrequencyMhz() {
+        return frequency;
+    }
+
+    /**
+     * Return the signal strength of probe response/beacon in (100 * dBm).
+     *
+     * @return Signal strenght in (100 * dBm).
+     */
+    public int getSignalMbm() {
+        return signalMbm;
+    }
+
+    /**
+     * Return the TSF (Timing Synchronization Function) of the received probe response/beacon.
+     * @return
+     */
+    public long getTsf() {
+        return tsf;
+    }
+
+    /**
+     * Return a boolean indicating whether or not we're associated to the AP represented by this
+     * scan result.
+     *
+     * @return A boolean indicating association.
+     */
+    public boolean isAssociated() {
+        return associated;
+    }
+
+    /**
+     *  Returns the capabilities of the AP repseresented by this scan result as advertised in the
+     *  received probe response or beacon.
+     *
+     *  This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 8.4.1.4:
+     *    Bit 0 - ESS
+     *    Bit 1 - IBSS
+     *    Bit 2 - CF Pollable
+     *    Bit 3 - CF-Poll Request
+     *    Bit 4 - Privacy
+     *    Bit 5 - Short Preamble
+     *    Bit 6 - PBCC
+     *    Bit 7 - Channel Agility
+     *    Bit 8 - Spectrum Mgmt
+     *    Bit 9 - QoS
+     *    Bit 10 - Short Slot Time
+     *    Bit 11 - APSD
+     *    Bit 12 - Radio Measurement
+     *    Bit 13 - DSSS-OFDM
+     *    Bit 14 - Delayed Block Ack
+     *    Bit 15 - Immediate Block Ack
+     *
+     * @return a bit mask of capabilities.
+     */
+    @NonNull public BitSet getCapabilities() {
+        return capability;
+    }
+
+    /**
+     * Returns details of the signal received on each radio chain for the AP represented by this
+     * scan result in a list of {@link RadioChainInfo} elements.
+     *
+     * @return A list of {@link RadioChainInfo} - possibly empty in case of error.
+     */
+    @NonNull public List<RadioChainInfo> getRadioChainInfos() {
+        return radioChainInfos;
+    }
+
+    /**
+     * @hide
+     */
+    public NativeScanResult() { }
+
     /** implement Parcelable interface */
     @Override
     public int describeContents() {
@@ -64,7 +181,7 @@
 
     /** implement Parcelable interface */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeByteArray(ssid);
         out.writeByteArray(bssid);
         out.writeByteArray(infoElement);
@@ -83,14 +200,23 @@
     }
 
     /** implement Parcelable interface */
-    public static final Parcelable.Creator<NativeScanResult> CREATOR =
+    @NonNull public static final Parcelable.Creator<NativeScanResult> CREATOR =
             new Parcelable.Creator<NativeScanResult>() {
         @Override
         public NativeScanResult createFromParcel(Parcel in) {
             NativeScanResult result = new NativeScanResult();
             result.ssid = in.createByteArray();
+            if (result.ssid == null) {
+                result.ssid = new byte[0];
+            }
             result.bssid = in.createByteArray();
+            if (result.bssid == null) {
+                result.bssid = new byte[0];
+            }
             result.infoElement = in.createByteArray();
+            if (result.infoElement == null) {
+                result.infoElement = new byte[0];
+            }
             result.frequency = in.readInt();
             result.signalMbm = in.readInt();
             result.tsf = in.readLong();
diff --git a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
index 4994ebd..554f929 100644
--- a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
+++ b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
@@ -16,21 +16,32 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.util.Arrays;
 
 /**
- * NativeWifiClient for wificond
+ * Structure providing information about clients (STAs) associated with a SoftAp.
  *
  * @hide
  */
-public class NativeWifiClient implements Parcelable {
-    public byte[] macAddress;
+@SystemApi
+public final class NativeWifiClient implements Parcelable {
+    /**
+     * The raw bytes of the MAC address of the client (STA) represented by this object.
+     */
+    @NonNull public final byte[] macAddress;
 
-    /** public constructor */
-    public NativeWifiClient() { }
+    /**
+     * public constructor
+     * @hide
+     */
+    public NativeWifiClient(@NonNull byte[] macAddress) {
+        this.macAddress = macAddress;
+    }
 
     /** override comparator */
     @Override
@@ -60,18 +71,20 @@
      * |flag| is ignored.
      */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeByteArray(macAddress);
     }
 
     /** implement Parcelable interface */
-    public static final Parcelable.Creator<NativeWifiClient> CREATOR =
+    @NonNull public static final Parcelable.Creator<NativeWifiClient> CREATOR =
             new Parcelable.Creator<NativeWifiClient>() {
                 @Override
                 public NativeWifiClient createFromParcel(Parcel in) {
-                    NativeWifiClient result = new NativeWifiClient();
-                    result.macAddress = in.createByteArray();
-                    return result;
+                    byte[] macAddress = in.createByteArray();
+                    if (macAddress == null) {
+                        macAddress = new byte[0];
+                    }
+                    return new NativeWifiClient(macAddress);
                 }
 
                 @Override
diff --git a/wifi/java/android/net/wifi/wificond/PnoNetwork.java b/wifi/java/android/net/wifi/wificond/PnoNetwork.java
index f923fd3..ca0b1cf 100644
--- a/wifi/java/android/net/wifi/wificond/PnoNetwork.java
+++ b/wifi/java/android/net/wifi/wificond/PnoNetwork.java
@@ -16,6 +16,8 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -23,17 +25,85 @@
 import java.util.Objects;
 
 /**
- * PnoNetwork for wificond
+ * Configuration for a PNO (preferred network offload) network used in {@link PnoSettings}. A PNO
+ * network allows configuration of a specific network to search for.
  *
  * @hide
  */
-public class PnoNetwork implements Parcelable {
+@SystemApi
+public final class PnoNetwork implements Parcelable {
+    private boolean mIsHidden;
+    private byte[] mSsid;
+    private int[] mFrequencies;
 
-    public boolean isHidden;
-    public byte[] ssid;
-    public int[] frequencies;
+    /**
+     * Indicates whether the PNO network configuration is for a hidden SSID - i.e. a network which
+     * does not broadcast its SSID and must be queried explicitly.
+     *
+     * @return True if the configuration is for a hidden network, false otherwise.
+     */
+    public boolean isHidden() {
+        return mIsHidden;
+    }
 
-    /** public constructor */
+    /**
+     * Configure whether the PNO network configuration is for a hidden SSID - i.e. a network which
+     * does not broadcast its SSID and must be queried explicitly.
+     *
+     * @param isHidden True if the configuration is for a hidden network, false otherwise.
+     */
+    public void setHidden(boolean isHidden) {
+        mIsHidden = isHidden;
+    }
+
+    /**
+     * Get the raw bytes for the SSID of the PNO network being scanned for.
+     *
+     * @return A byte array.
+     */
+    @NonNull public byte[] getSsid() {
+        return mSsid;
+    }
+
+    /**
+     * Set the raw bytes for the SSID of the PNO network being scanned for.
+     *
+     * @param ssid A byte array.
+     */
+    public void setSsid(@NonNull byte[] ssid) {
+        if (ssid == null) {
+            throw new IllegalArgumentException("null argument");
+        }
+        this.mSsid = ssid;
+    }
+
+    /**
+     * Get the frequencies (in MHz) on which to PNO scan for the current network is being searched
+     * for. A null return (i.e. no frequencies configured) indicates that the network is search for
+     * on all supported frequencies.
+     *
+     * @return A array of frequencies (in MHz), a null indicates no configured frequencies.
+     */
+    @NonNull public int[] getFrequenciesMhz() {
+        return mFrequencies;
+    }
+
+    /**
+     * Set the frequencies (in MHz) on which to PNO scan for the current network is being searched
+     * for. A null configuration (i.e. no frequencies configured) indicates that the network is
+     * search for on all supported frequencies.
+     *
+     * @param frequenciesMhz an array of frequencies (in MHz), null indicating no configured
+     *                       frequencies.
+     */
+    public void setFrequenciesMhz(@NonNull int[] frequenciesMhz) {
+        if (frequenciesMhz == null) {
+            throw new IllegalArgumentException("null argument");
+        }
+        this.mFrequencies = frequenciesMhz;
+    }
+
+    /** Construct an uninitialized PnoNetwork object */
     public PnoNetwork() { }
 
     /** override comparator */
@@ -44,18 +114,18 @@
             return false;
         }
         PnoNetwork network = (PnoNetwork) rhs;
-        return Arrays.equals(ssid, network.ssid)
-                && Arrays.equals(frequencies, network.frequencies)
-                && isHidden == network.isHidden;
+        return Arrays.equals(mSsid, network.mSsid)
+                && Arrays.equals(mFrequencies, network.mFrequencies)
+                && mIsHidden == network.mIsHidden;
     }
 
     /** override hash code */
     @Override
     public int hashCode() {
         return Objects.hash(
-                isHidden,
-                Arrays.hashCode(ssid),
-                Arrays.hashCode(frequencies));
+                mIsHidden,
+                Arrays.hashCode(mSsid),
+                Arrays.hashCode(mFrequencies));
     }
 
     /** implement Parcelable interface */
@@ -69,21 +139,27 @@
      * |flag| is ignored.
      */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(isHidden ? 1 : 0);
-        out.writeByteArray(ssid);
-        out.writeIntArray(frequencies);
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mIsHidden ? 1 : 0);
+        out.writeByteArray(mSsid);
+        out.writeIntArray(mFrequencies);
     }
 
     /** implement Parcelable interface */
-    public static final Parcelable.Creator<PnoNetwork> CREATOR =
+    @NonNull public static final Parcelable.Creator<PnoNetwork> CREATOR =
             new Parcelable.Creator<PnoNetwork>() {
         @Override
         public PnoNetwork createFromParcel(Parcel in) {
             PnoNetwork result = new PnoNetwork();
-            result.isHidden = in.readInt() != 0 ? true : false;
-            result.ssid = in.createByteArray();
-            result.frequencies = in.createIntArray();
+            result.mIsHidden = in.readInt() != 0 ? true : false;
+            result.mSsid = in.createByteArray();
+            if (result.mSsid == null) {
+                result.mSsid = new byte[0];
+            }
+            result.mFrequencies = in.createIntArray();
+            if (result.mFrequencies == null) {
+                result.mFrequencies = new int[0];
+            }
             return result;
         }
 
diff --git a/wifi/java/android/net/wifi/wificond/PnoSettings.java b/wifi/java/android/net/wifi/wificond/PnoSettings.java
index 96cf24f..57c9ca5 100644
--- a/wifi/java/android/net/wifi/wificond/PnoSettings.java
+++ b/wifi/java/android/net/wifi/wificond/PnoSettings.java
@@ -16,27 +16,130 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
- * PnoSettings for wificond
+ * Configuration for a PNO (preferred network offload). A mechanism by which scans are offloaded
+ * from the host device to the Wi-Fi chip.
  *
  * @hide
  */
-public class PnoSettings implements Parcelable {
-    public int intervalMs;
-    public int min2gRssi;
-    public int min5gRssi;
-    public int min6gRssi;
-    public ArrayList<PnoNetwork> pnoNetworks;
+@SystemApi
+public final class PnoSettings implements Parcelable {
+    private int mIntervalMs;
+    private int mMin2gRssi;
+    private int mMin5gRssi;
+    private int mMin6gRssi;
+    private List<PnoNetwork> mPnoNetworks;
 
-    /** public constructor */
+    /** Construct an uninitialized PnoSettings object */
     public PnoSettings() { }
 
+    /**
+     * Get the requested PNO scan interval in milliseconds.
+     *
+     * @return An interval in milliseconds.
+     */
+    public int getIntervalMillis() {
+        return mIntervalMs;
+    }
+
+    /**
+     * Set the requested PNO scan interval in milliseconds.
+     *
+     * @param intervalMs An interval in milliseconds.
+     */
+    public void setIntervalMillis(int intervalMs) {
+        this.mIntervalMs = intervalMs;
+    }
+
+    /**
+     * Get the requested minimum RSSI threshold (in dBm) for APs to report in scan results in the
+     * 2.4GHz band.
+     *
+     * @return An RSSI value in dBm.
+     */
+    public int getMin2gRssiDbm() {
+        return mMin2gRssi;
+    }
+
+    /**
+     * Set the requested minimum RSSI threshold (in dBm) for APs to report in scan scan results in
+     * the 2.4GHz band.
+     *
+     * @param min2gRssiDbm An RSSI value in dBm.
+     */
+    public void setMin2gRssiDbm(int min2gRssiDbm) {
+        this.mMin2gRssi = min2gRssiDbm;
+    }
+
+    /**
+     * Get the requested minimum RSSI threshold (in dBm) for APs to report in scan results in the
+     * 5GHz band.
+     *
+     * @return An RSSI value in dBm.
+     */
+    public int getMin5gRssiDbm() {
+        return mMin5gRssi;
+    }
+
+    /**
+     * Set the requested minimum RSSI threshold (in dBm) for APs to report in scan scan results in
+     * the 5GHz band.
+     *
+     * @param min5gRssiDbm An RSSI value in dBm.
+     */
+    public void setMin5gRssiDbm(int min5gRssiDbm) {
+        this.mMin5gRssi = min5gRssiDbm;
+    }
+
+    /**
+     * Get the requested minimum RSSI threshold (in dBm) for APs to report in scan results in the
+     * 6GHz band.
+     *
+     * @return An RSSI value in dBm.
+     */
+    public int getMin6gRssiDbm() {
+        return mMin6gRssi;
+    }
+
+    /**
+     * Set the requested minimum RSSI threshold (in dBm) for APs to report in scan scan results in
+     * the 6GHz band.
+     *
+     * @param min6gRssiDbm An RSSI value in dBm.
+     */
+    public void setMin6gRssiDbm(int min6gRssiDbm) {
+        this.mMin6gRssi = min6gRssiDbm;
+    }
+
+    /**
+     * Return the configured list of specific networks to search for in a PNO scan.
+     *
+     * @return A list of {@link PnoNetwork} objects, possibly empty if non configured.
+     */
+    @NonNull public List<PnoNetwork> getPnoNetworks() {
+        return mPnoNetworks;
+    }
+
+    /**
+     * Set the list of specified networks to scan for in a PNO scan. The networks (APs) are
+     * specified using {@link PnoNetwork}s. An empty list indicates that all networks are scanned
+     * for.
+     *
+     * @param pnoNetworks A (possibly empty) list of {@link PnoNetwork} objects.
+     */
+    public void setPnoNetworks(@NonNull List<PnoNetwork> pnoNetworks) {
+        this.mPnoNetworks = pnoNetworks;
+    }
+
     /** override comparator */
     @Override
     public boolean equals(Object rhs) {
@@ -48,17 +151,17 @@
         if (settings == null) {
             return false;
         }
-        return intervalMs == settings.intervalMs
-                && min2gRssi == settings.min2gRssi
-                && min5gRssi == settings.min5gRssi
-                && min6gRssi == settings.min6gRssi
-                && pnoNetworks.equals(settings.pnoNetworks);
+        return mIntervalMs == settings.mIntervalMs
+                && mMin2gRssi == settings.mMin2gRssi
+                && mMin5gRssi == settings.mMin5gRssi
+                && mMin6gRssi == settings.mMin6gRssi
+                && mPnoNetworks.equals(settings.mPnoNetworks);
     }
 
     /** override hash code */
     @Override
     public int hashCode() {
-        return Objects.hash(intervalMs, min2gRssi, min5gRssi, min6gRssi, pnoNetworks);
+        return Objects.hash(mIntervalMs, mMin2gRssi, mMin5gRssi, mMin6gRssi, mPnoNetworks);
     }
 
     /** implement Parcelable interface */
@@ -72,27 +175,27 @@
      * |flag| is ignored.
      **/
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(intervalMs);
-        out.writeInt(min2gRssi);
-        out.writeInt(min5gRssi);
-        out.writeInt(min6gRssi);
-        out.writeTypedList(pnoNetworks);
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mIntervalMs);
+        out.writeInt(mMin2gRssi);
+        out.writeInt(mMin5gRssi);
+        out.writeInt(mMin6gRssi);
+        out.writeTypedList(mPnoNetworks);
     }
 
     /** implement Parcelable interface */
-    public static final Parcelable.Creator<PnoSettings> CREATOR =
+    @NonNull public static final Parcelable.Creator<PnoSettings> CREATOR =
             new Parcelable.Creator<PnoSettings>() {
         @Override
         public PnoSettings createFromParcel(Parcel in) {
             PnoSettings result = new PnoSettings();
-            result.intervalMs = in.readInt();
-            result.min2gRssi = in.readInt();
-            result.min5gRssi = in.readInt();
-            result.min6gRssi = in.readInt();
+            result.mIntervalMs = in.readInt();
+            result.mMin2gRssi = in.readInt();
+            result.mMin5gRssi = in.readInt();
+            result.mMin6gRssi = in.readInt();
 
-            result.pnoNetworks = new ArrayList<PnoNetwork>();
-            in.readTypedList(result.pnoNetworks, PnoNetwork.CREATOR);
+            result.mPnoNetworks = new ArrayList<>();
+            in.readTypedList(result.mPnoNetworks, PnoNetwork.CREATOR);
 
             return result;
         }
diff --git a/wifi/java/android/net/wifi/wificond/RadioChainInfo.java b/wifi/java/android/net/wifi/wificond/RadioChainInfo.java
index 2b03450..64102dd 100644
--- a/wifi/java/android/net/wifi/wificond/RadioChainInfo.java
+++ b/wifi/java/android/net/wifi/wificond/RadioChainInfo.java
@@ -16,26 +16,52 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Objects;
 
 /**
- * RadioChainInfo for wificond
+ * A class representing the radio chains of the Wi-Fi modems. Use to provide raw information about
+ * signals received on different radio chains.
  *
  * @hide
  */
-public class RadioChainInfo implements Parcelable {
+@SystemApi
+public final class RadioChainInfo implements Parcelable {
     private static final String TAG = "RadioChainInfo";
 
+    /** @hide */
+    @VisibleForTesting
     public int chainId;
+    /** @hide */
+    @VisibleForTesting
     public int level;
 
+    /**
+     * Return an identifier for this radio chain. This is an arbitrary ID which is consistent for
+     * the same device.
+     *
+     * @return The radio chain ID.
+     */
+    public int getChainId() {
+        return chainId;
+    }
 
-    /** public constructor */
-    public RadioChainInfo() { }
+    /**
+     * Returns the detected signal level on this radio chain in dBm (aka RSSI).
+     *
+     * @return A signal level in dBm.
+     */
+    public int getLevelDbm() {
+        return level;
+    }
 
+    /** @hide */
     public RadioChainInfo(int chainId, int level) {
         this.chainId = chainId;
         this.level = level;
@@ -73,23 +99,20 @@
      * |flags| is ignored.
      */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeInt(chainId);
         out.writeInt(level);
     }
 
     /** implement Parcelable interface */
-    public static final Parcelable.Creator<RadioChainInfo> CREATOR =
+    @NonNull public static final Parcelable.Creator<RadioChainInfo> CREATOR =
             new Parcelable.Creator<RadioChainInfo>() {
         /**
          * Caller is responsible for providing a valid parcel.
          */
         @Override
         public RadioChainInfo createFromParcel(Parcel in) {
-            RadioChainInfo result = new RadioChainInfo();
-            result.chainId = in.readInt();
-            result.level = in.readInt();
-            return result;
+            return new RadioChainInfo(in.readInt(), in.readInt());
         }
 
         @Override
diff --git a/wifi/java/android/net/wifi/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
similarity index 61%
rename from wifi/java/android/net/wifi/WifiCondManager.java
rename to wifi/java/android/net/wifi/wificond/WifiCondManager.java
index c05ba34..94f1212 100644
--- a/wifi/java/android/net/wifi/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -14,18 +14,27 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.net.wifi.wificond;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
 import android.app.AlarmManager;
 import android.content.Context;
-import android.net.wifi.wificond.ChannelSettings;
-import android.net.wifi.wificond.HiddenNetwork;
-import android.net.wifi.wificond.NativeScanResult;
-import android.net.wifi.wificond.NativeWifiClient;
-import android.net.wifi.wificond.PnoSettings;
-import android.net.wifi.wificond.SingleScanSettings;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IApInterfaceEventCallback;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.IPnoScanEvent;
+import android.net.wifi.IScanEvent;
+import android.net.wifi.ISendMgmtFrameEvent;
+import android.net.wifi.IWifiScannerImpl;
+import android.net.wifi.IWificond;
+import android.net.wifi.SoftApInfo;
+import android.net.wifi.WifiAnnotations;
+import android.net.wifi.WifiScanner;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -44,37 +53,48 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * This class provides methods for WifiNative to send control commands to wificond.
- * NOTE: This class should only be used from WifiNative.
+ * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The
+ * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions.
+ *
  * @hide
  */
-public class WifiCondManager implements IBinder.DeathRecipient {
+@SystemApi
+@SystemService(Context.WIFI_COND_SERVICE)
+public class WifiCondManager {
     private static final String TAG = "WifiCondManager";
     private boolean mVerboseLoggingEnabled = false;
 
     /**
-     * The {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()}
+     * The {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}
      * timeout, in milliseconds, after which
      * {@link SendMgmtFrameCallback#onFailure(int)} will be called with reason
      * {@link #SEND_MGMT_FRAME_ERROR_TIMEOUT}.
      */
-    public static final int SEND_MGMT_FRAME_TIMEOUT_MS = 1000;
+    private static final int SEND_MGMT_FRAME_TIMEOUT_MS = 1000;
 
     private static final String TIMEOUT_ALARM_TAG = TAG + " Send Management Frame Timeout";
 
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"SCAN_TYPE_"},
             value = {SCAN_TYPE_SINGLE_SCAN,
                     SCAN_TYPE_PNO_SCAN})
     public @interface ScanResultType {}
 
-    /** Get scan results for a single scan */
+    /**
+     * Specifies a scan type: single scan initiated by the framework. Can be used in
+     * {@link #getScanResults(String, int)} to specify the type of scan result to fetch.
+     */
     public static final int SCAN_TYPE_SINGLE_SCAN = 0;
 
-    /** Get scan results for Pno Scan */
+    /**
+     * Specifies a scan type: PNO scan. Can be used in {@link #getScanResults(String, int)} to
+     * specify the type of scan result to fetch.
+     */
     public static final int SCAN_TYPE_PNO_SCAN = 1;
 
     private AlarmManager mAlarmManager;
@@ -95,11 +115,12 @@
     private AtomicBoolean mSendMgmtFrameInProgress = new AtomicBoolean(false);
 
     /**
-     * Interface for a callback to be used to handle scan results.
+     * Interface used when waiting for scans to be completed (with results).
      */
     public interface ScanEventCallback {
         /**
-         * Called when scan results are available.
+         * Called when scan results are available. Scans results should then be obtained from
+         * {@link #getScanResults(String, int)}.
          */
         void onScanResultReady();
 
@@ -110,11 +131,14 @@
     }
 
     /**
-     * Interface for a callback to provide information about PNO scan request.
+     * Interface for a callback to provide information about PNO scan request requested with
+     * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}. Note that the
+     * callback are for the status of the request - not the scan itself. The results of the scan
+     * are returned with {@link ScanEventCallback}.
      */
     public interface PnoScanRequestCallback {
         /**
-         * Called when the PNO scan is requested.
+         * Called when a PNO scan request has been successfully submitted.
          */
         void onPnoRequestSucceeded();
 
@@ -125,73 +149,116 @@
     }
 
     private class ScanEventHandler extends IScanEvent.Stub {
+        private Executor mExecutor;
         private ScanEventCallback mCallback;
 
-        ScanEventHandler(@NonNull ScanEventCallback callback) {
+        ScanEventHandler(@NonNull Executor executor, @NonNull ScanEventCallback callback) {
+            mExecutor = executor;
             mCallback = callback;
         }
 
         @Override
         public void OnScanResultReady() {
             Log.d(TAG, "Scan result ready event");
-            mCallback.onScanResultReady();
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onScanResultReady());
         }
 
         @Override
         public void OnScanFailed() {
             Log.d(TAG, "Scan failed event");
-            mCallback.onScanFailed();
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onScanFailed());
         }
     }
 
     /**
-     * Result of a signal poll.
+     * Result of a signal poll requested using {@link #signalPoll(String)}.
      */
     public static class SignalPollResult {
-        // RSSI value in dBM.
-        public int currentRssi;
-        //Transmission bit rate in Mbps.
-        public int txBitrate;
-        // Association frequency in MHz.
-        public int associationFrequency;
-        //Last received packet bit rate in Mbps.
-        public int rxBitrate;
+        /** @hide */
+        public SignalPollResult(int currentRssiDbm, int txBitrateMbps, int rxBitrateMbps,
+                int associationFrequencyMHz) {
+            this.currentRssiDbm = currentRssiDbm;
+            this.txBitrateMbps = txBitrateMbps;
+            this.rxBitrateMbps = rxBitrateMbps;
+            this.associationFrequencyMHz = associationFrequencyMHz;
+        }
+
+        /**
+         * RSSI value in dBM.
+         */
+        public final int currentRssiDbm;
+
+        /**
+         * Transmission bit rate in Mbps.
+         */
+        public final int txBitrateMbps;
+
+        /**
+         * Last received packet bit rate in Mbps.
+         */
+        public final int rxBitrateMbps;
+
+        /**
+         * Association frequency in MHz.
+         */
+        public final int associationFrequencyMHz;
     }
 
     /**
-     * WiFi interface transimission counters.
+     * Transmission counters obtained using {@link #getTxPacketCounters(String)}.
      */
     public static class TxPacketCounters {
-        // Number of successfully transmitted packets.
-        public int txSucceeded;
-        // Number of tramsmission failures.
-        public int txFailed;
+        /** @hide */
+        public TxPacketCounters(int txPacketSucceeded, int txPacketFailed) {
+            this.txPacketSucceeded = txPacketSucceeded;
+            this.txPacketFailed = txPacketFailed;
+        }
+
+        /**
+         * Number of successfully transmitted packets.
+         */
+        public final int txPacketSucceeded;
+
+        /**
+         * Number of packet transmission failures.
+         */
+        public final int txPacketFailed;
     }
 
     /**
-     * Callbacks for SoftAp interface.
+     * Callbacks for SoftAp interface registered using
+     * {@link #registerApCallback(String, Executor, SoftApCallback)}.
      */
-    public interface SoftApListener {
+    public interface SoftApCallback {
         /**
-         * Invoked when there is some fatal failure in the lower layers.
+         * Invoked when there is a fatal failure and the SoftAp is shutdown.
          */
         void onFailure();
 
         /**
-         * Invoked when the associated stations changes.
+         * Invoked when there is a change in the associated station (STA).
+         * @param client Information about the client whose status has changed.
+         * @param isConnected Indication as to whether the client is connected (true), or
+         *                    disconnected (false).
          */
-        void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected);
+        void onConnectedClientsChanged(@NonNull NativeWifiClient client, boolean isConnected);
 
         /**
-         * Invoked when the channel switch event happens.
+         * Invoked when a channel switch event happens - i.e. the SoftAp is moved to a different
+         * channel. Also called on initial registration.
+         * @param frequencyMhz The new frequency of the SoftAp. A value of 0 is invalid and is an
+         *                     indication that the SoftAp is not enabled.
+         * @param bandwidth The new bandwidth of the SoftAp.
          */
-        void onSoftApChannelSwitched(int frequency, int bandwidth);
+        void onSoftApChannelSwitched(int frequencyMhz, @WifiAnnotations.Bandwidth int bandwidth);
     }
 
     /**
      * Callback to notify the results of a
-     * {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()} call.
-     * Note: no callbacks will be triggered if the iface dies while sending a frame.
+     * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} call.
+     * Note: no callbacks will be triggered if the interface dies while sending a frame.
      */
     public interface SendMgmtFrameCallback {
         /**
@@ -211,6 +278,7 @@
         void onFailure(@SendMgmtFrameError int reason);
     }
 
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"SEND_MGMT_FRAME_ERROR_"},
             value = {SEND_MGMT_FRAME_ERROR_UNKNOWN,
@@ -224,43 +292,44 @@
 
     /**
      * Unknown error occurred during call to
-     * {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()}.
+     * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}.
      */
     public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1;
 
     /**
      * Specifying the MCS rate in
-     * {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()} is not
+     * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)} is not
      * supported by this device.
      */
     public static final int SEND_MGMT_FRAME_ERROR_MCS_UNSUPPORTED = 2;
 
     /**
      * Driver reported that no ACK was received for the frame transmitted using
-     * {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()}.
+     * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}.
      */
     public static final int SEND_MGMT_FRAME_ERROR_NO_ACK = 3;
 
     /**
      * Error code for when the driver fails to report on the status of the frame sent by
-     * {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()}
+     * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}
      * after {@link #SEND_MGMT_FRAME_TIMEOUT_MS} milliseconds.
      */
     public static final int SEND_MGMT_FRAME_ERROR_TIMEOUT = 4;
 
     /**
      * An existing call to
-     * {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int) sendMgmtFrame()}
+     * {@link #sendMgmtFrame(String, byte[], int, Executor, SendMgmtFrameCallback)}
      * is in progress. Another frame cannot be sent until the first call completes.
      */
     public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5;
 
-
+    /** @hide */
     public WifiCondManager(Context context) {
-        mAlarmManager = (AlarmManager) context.getSystemService(AlarmManager.class);
+        mAlarmManager = context.getSystemService(AlarmManager.class);
         mEventHandler = new Handler(context.getMainLooper());
     }
 
+    /** @hide */
     @VisibleForTesting
     public WifiCondManager(Context context, IWificond wificond) {
         this(context);
@@ -268,22 +337,26 @@
     }
 
     private class PnoScanEventHandler extends IPnoScanEvent.Stub {
+        private Executor mExecutor;
         private ScanEventCallback mCallback;
 
-        PnoScanEventHandler(@NonNull ScanEventCallback callback) {
+        PnoScanEventHandler(@NonNull Executor executor, @NonNull ScanEventCallback callback) {
+            mExecutor = executor;
             mCallback = callback;
         }
 
         @Override
         public void OnPnoNetworkFound() {
             Log.d(TAG, "Pno scan result event");
-            mCallback.onScanResultReady();
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onScanResultReady());
         }
 
         @Override
         public void OnPnoScanFailed() {
             Log.d(TAG, "Pno Scan failed event");
-            mCallback.onScanFailed();
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onScanFailed());
         }
     }
 
@@ -291,9 +364,11 @@
      * Listener for AP Interface events.
      */
     private class ApInterfaceEventCallback extends IApInterfaceEventCallback.Stub {
-        private SoftApListener mSoftApListener;
+        private Executor mExecutor;
+        private SoftApCallback mSoftApListener;
 
-        ApInterfaceEventCallback(SoftApListener listener) {
+        ApInterfaceEventCallback(Executor executor, SoftApCallback listener) {
+            mExecutor = executor;
             mSoftApListener = listener;
         }
 
@@ -304,12 +379,36 @@
                         + client.macAddress + " isConnected: " + isConnected);
             }
 
-            mSoftApListener.onConnectedClientsChanged(client, isConnected);
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mSoftApListener.onConnectedClientsChanged(client, isConnected));
         }
 
         @Override
         public void onSoftApChannelSwitched(int frequency, int bandwidth) {
-            mSoftApListener.onSoftApChannelSwitched(frequency, bandwidth);
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> mSoftApListener.onSoftApChannelSwitched(frequency,
+                    toFrameworkBandwidth(bandwidth)));
+        }
+
+        private @WifiAnnotations.Bandwidth int toFrameworkBandwidth(int bandwidth) {
+            switch(bandwidth) {
+                case IApInterfaceEventCallback.BANDWIDTH_INVALID:
+                    return SoftApInfo.CHANNEL_WIDTH_INVALID;
+                case IApInterfaceEventCallback.BANDWIDTH_20_NOHT:
+                    return SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT;
+                case IApInterfaceEventCallback.BANDWIDTH_20:
+                    return SoftApInfo.CHANNEL_WIDTH_20MHZ;
+                case IApInterfaceEventCallback.BANDWIDTH_40:
+                    return SoftApInfo.CHANNEL_WIDTH_40MHZ;
+                case IApInterfaceEventCallback.BANDWIDTH_80:
+                    return SoftApInfo.CHANNEL_WIDTH_80MHZ;
+                case IApInterfaceEventCallback.BANDWIDTH_80P80:
+                    return SoftApInfo.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
+                case IApInterfaceEventCallback.BANDWIDTH_160:
+                    return SoftApInfo.CHANNEL_WIDTH_160MHZ;
+                default:
+                    return SoftApInfo.CHANNEL_WIDTH_INVALID;
+            }
         }
     }
 
@@ -317,6 +416,7 @@
      * Callback triggered by wificond.
      */
     private class SendMgmtFrameEvent extends ISendMgmtFrameEvent.Stub {
+        private Executor mExecutor;
         private SendMgmtFrameCallback mCallback;
         private AlarmManager.OnAlarmListener mTimeoutCallback;
         /**
@@ -332,14 +432,16 @@
             r.run();
         }
 
-        SendMgmtFrameEvent(@NonNull SendMgmtFrameCallback callback) {
+        SendMgmtFrameEvent(@NonNull Executor executor, @NonNull SendMgmtFrameCallback callback) {
+            mExecutor = executor;
             mCallback = callback;
             // called in main thread
             mTimeoutCallback = () -> runIfFirstCall(() -> {
                 if (mVerboseLoggingEnabled) {
                     Log.e(TAG, "Timed out waiting for ACK");
                 }
-                mCallback.onFailure(SEND_MGMT_FRAME_ERROR_TIMEOUT);
+                Binder.clearCallingIdentity();
+                mExecutor.execute(() -> mCallback.onFailure(SEND_MGMT_FRAME_ERROR_TIMEOUT));
             });
             mWasCalled = false;
 
@@ -354,7 +456,8 @@
             // post to main thread
             mEventHandler.post(() -> runIfFirstCall(() -> {
                 mAlarmManager.cancel(mTimeoutCallback);
-                mCallback.onAck(elapsedTimeMs);
+                Binder.clearCallingIdentity();
+                mExecutor.execute(() -> mCallback.onAck(elapsedTimeMs));
             }));
         }
 
@@ -364,7 +467,8 @@
             // post to main thread
             mEventHandler.post(() -> runIfFirstCall(() -> {
                 mAlarmManager.cancel(mTimeoutCallback);
-                mCallback.onFailure(reason);
+                Binder.clearCallingIdentity();
+                mExecutor.execute(() -> mCallback.onFailure(reason));
             }));
         }
     }
@@ -372,8 +476,9 @@
     /**
      * Called by the binder subsystem upon remote object death.
      * Invoke all the register death handlers and clear state.
+     * @hide
      */
-    @Override
+    @VisibleForTesting
     public void binderDied() {
         mEventHandler.post(() -> {
             Log.e(TAG, "Wificond died!");
@@ -387,17 +492,22 @@
         });
     }
 
-    /** Enable or disable verbose logging of WificondControl.
-     *  @param enable True to enable verbose logging. False to disable verbose logging.
+    /**
+     * Enable or disable verbose logging of the WifiCondManager module.
+     * @param enable True to enable verbose logging. False to disable verbose logging.
      */
     public void enableVerboseLogging(boolean enable) {
         mVerboseLoggingEnabled = enable;
     }
 
     /**
-     * Initializes wificond & registers a death notification for wificond.
-     * This method clears any existing state in wificond daemon.
+     * Initializes WifiCondManager & registers a death notification for the WifiCondManager which
+     * acts as a proxy for the wificond daemon (i.e. the death listener will be called when and if
+     * the wificond daemon dies).
      *
+     * Note: This method clears any existing state in wificond daemon.
+     *
+     * @param deathEventHandler A {@link Runnable} to be called whenever the wificond daemon dies.
      * @return Returns true on success.
      */
     public boolean initialize(@NonNull Runnable deathEventHandler) {
@@ -428,7 +538,7 @@
             return false;
         }
         try {
-            mWificond.asBinder().linkToDeath(this, 0);
+            mWificond.asBinder().linkToDeath(() -> binderDied(), 0);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register death notification for wificond");
             // The remote has already died.
@@ -438,16 +548,27 @@
     }
 
     /**
-    * Setup interface for client mode via wificond.
-    * @return true on success.
-    */
+     * Set up an interface for client (STA) mode.
+     *
+     * @param ifaceName Name of the interface to configure.
+     * @param executor The Executor on which to execute the callbacks.
+     * @param scanCallback A callback for framework initiated scans.
+     * @param pnoScanCallback A callback for PNO (offloaded) scans.
+     * @return true on success.
+     */
     public boolean setupInterfaceForClientMode(@NonNull String ifaceName,
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback) {
         Log.d(TAG, "Setting up interface for client mode");
         if (!retrieveWificondAndRegisterForDeath()) {
             return false;
         }
 
+        if (scanCallback == null || pnoScanCallback == null || executor == null) {
+            Log.e(TAG, "setupInterfaceForClientMode invoked with null callbacks");
+            return false;
+        }
+
         IClientInterface clientInterface = null;
         try {
             clientInterface = mWificond.createClientInterface(ifaceName);
@@ -472,10 +593,11 @@
             }
             mWificondScanners.put(ifaceName, wificondScanner);
             Binder.allowBlocking(wificondScanner.asBinder());
-            ScanEventHandler scanEventHandler = new ScanEventHandler(scanCallback);
+            ScanEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback);
             mScanEventHandlers.put(ifaceName,  scanEventHandler);
             wificondScanner.subscribeScanEvents(scanEventHandler);
-            PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(pnoScanCallback);
+            PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor,
+                    pnoScanCallback);
             mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
             wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
         } catch (RemoteException e) {
@@ -486,8 +608,10 @@
     }
 
     /**
-     * Teardown a specific STA interface configured in wificond.
+     * Tear down a specific client (STA) interface, initially configured using
+     * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}.
      *
+     * @param ifaceName Name of the interface to tear down.
      * @return Returns true on success.
      */
     public boolean tearDownClientInterface(@NonNull String ifaceName) {
@@ -531,9 +655,11 @@
     }
 
     /**
-    * Setup interface for softAp mode via wificond.
-    * @return true on success.
-    */
+     * Set up interface as a Soft AP.
+     *
+     * @param ifaceName Name of the interface to configure.
+     * @return true on success.
+     */
     public boolean setupInterfaceForSoftApMode(@NonNull String ifaceName) {
         Log.d(TAG, "Setting up interface for soft ap mode");
         if (!retrieveWificondAndRegisterForDeath()) {
@@ -560,8 +686,10 @@
     }
 
     /**
-     * Teardown a specific AP interface configured in wificond.
+     * Tear down a Soft AP interface initially configured using
+     * {@link #setupInterfaceForSoftApMode(String)}.
      *
+     * @param ifaceName Name of the interface to tear down.
      * @return Returns true on success.
      */
     public boolean tearDownSoftApInterface(@NonNull String ifaceName) {
@@ -592,7 +720,8 @@
     }
 
     /**
-    * Teardown all interfaces configured in wificond.
+    * Tear down all interfaces, whether clients (STA) or Soft AP.
+     *
     * @return Returns true on success.
     */
     public boolean tearDownInterfaces() {
@@ -624,12 +753,13 @@
     }
 
     /**
-     * Request signal polling to wificond.
-     * @param ifaceName Name of the interface.
-     * Returns an SignalPollResult object.
-     * Returns null on failure.
+     * Request signal polling.
+     *
+     * @param ifaceName Name of the interface on which to poll.
+     * @return A {@link SignalPollResult} object containing interface statistics, or a null on
+     * error.
      */
-    public SignalPollResult signalPoll(@NonNull String ifaceName) {
+    @Nullable public SignalPollResult signalPoll(@NonNull String ifaceName) {
         IClientInterface iface = getClientInterface(ifaceName);
         if (iface == null) {
             Log.e(TAG, "No valid wificond client interface handler");
@@ -647,21 +777,16 @@
             Log.e(TAG, "Failed to do signal polling due to remote exception");
             return null;
         }
-        SignalPollResult pollResult = new SignalPollResult();
-        pollResult.currentRssi = resultArray[0];
-        pollResult.txBitrate = resultArray[1];
-        pollResult.associationFrequency = resultArray[2];
-        pollResult.rxBitrate = resultArray[3];
-        return pollResult;
+        return new SignalPollResult(resultArray[0], resultArray[1], resultArray[3], resultArray[2]);
     }
 
     /**
-     * Fetch TX packet counters on current connection from wificond.
+     * Get current transmit (Tx) packet counters of the specified interface.
+     *
      * @param ifaceName Name of the interface.
-     * Returns an TxPacketCounters object.
-     * Returns null on failure.
+     * @return {@link TxPacketCounters} of the current interface or null on error.
      */
-    public TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) {
+    @Nullable public TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) {
         IClientInterface iface = getClientInterface(ifaceName);
         if (iface == null) {
             Log.e(TAG, "No valid wificond client interface handler");
@@ -679,10 +804,7 @@
             Log.e(TAG, "Failed to do signal polling due to remote exception");
             return null;
         }
-        TxPacketCounters counters = new TxPacketCounters();
-        counters.txSucceeded = resultArray[0];
-        counters.txFailed = resultArray[1];
-        return counters;
+        return new TxPacketCounters(resultArray[0], resultArray[1]);
     }
 
     /** Helper function to look up the scanner impl handle using name */
@@ -691,10 +813,16 @@
     }
 
     /**
-    * Fetch the latest scan result from kernel via wificond.
-    * @param ifaceName Name of the interface.
-    * @return Returns an array of native scan results or an empty array on failure.
-    */
+     * Fetch the latest scan results of the indicated type for the specified interface. Note that
+     * this method fetches the latest results - it does not initiate a scan. Initiating a scan can
+     * be done using {@link #startScan(String, int, Set, List)} or
+     * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}.
+     *
+     * @param ifaceName Name of the interface.
+     * @param scanType The type of scan result to be returned, can be
+     * {@link #SCAN_TYPE_SINGLE_SCAN} or {@link #SCAN_TYPE_PNO_SCAN}.
+     * @return Returns an array of {@link NativeScanResult} or an empty array on failure.
+     */
     @NonNull public List<NativeScanResult> getScanResults(@NonNull String ifaceName,
             @ScanResultType int scanType) {
         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
@@ -739,15 +867,23 @@
     }
 
     /**
-     * Start a scan using wificond for the given parameters.
-     * @param ifaceName Name of the interface.
-     * @param scanType Type of scan to perform.
+     * Start a scan using the specified parameters. A scan is an asynchronous operation. The
+     * result of the operation is returned in the {@link ScanEventCallback} registered when
+     * setting up an interface using
+     * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}.
+     * The latest scans can be obtained using {@link #getScanResults(String, int)} and using a
+     * {@link #SCAN_TYPE_SINGLE_SCAN} for the {@code scanType}.
+     *
+     * @param ifaceName Name of the interface on which to initiate the scan.
+     * @param scanType Type of scan to perform, can be any of
+     * {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}, {@link WifiScanner#SCAN_TYPE_LOW_POWER}, or
+     * {@link WifiScanner#SCAN_TYPE_LOW_LATENCY}.
      * @param freqs list of frequencies to scan for, if null scan all supported channels.
      * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
      * @return Returns true on success.
      */
-    public boolean scan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
-            Set<Integer> freqs, List<byte[]> hiddenNetworkSSIDs) {
+    public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
+            @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) {
         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
         if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
@@ -792,25 +928,40 @@
     }
 
     /**
-     * Start PNO scan.
-     * @param ifaceName Name of the interface.
-     * @param pnoSettings Pno scan configuration.
+     * Request a PNO (Preferred Network Offload). The offload request and the scans are asynchronous
+     * operations. The result of the request are returned in the {@code callback} parameter which
+     * is an {@link PnoScanRequestCallback}. The scan results are are return in the
+     * {@link ScanEventCallback} which is registered when setting up an interface using
+     * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}.
+     * The latest PNO scans can be obtained using {@link #getScanResults(String, int)} with the
+     * {@code scanType} set to {@link #SCAN_TYPE_PNO_SCAN}.
+     *
+     * @param ifaceName Name of the interface on which to request a PNO.
+     * @param pnoSettings PNO scan configuration.
+     * @param executor The Executor on which to execute the callback.
+     * @param callback Callback for the results of the offload request.
      * @return true on success.
      */
-    public boolean startPnoScan(@NonNull String ifaceName, PnoSettings pnoSettings,
-            PnoScanRequestCallback callback) {
+    public boolean startPnoScan(@NonNull String ifaceName, @NonNull PnoSettings pnoSettings,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull PnoScanRequestCallback callback) {
         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
         if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return false;
         }
 
+        if (callback == null || executor == null) {
+            Log.e(TAG, "startPnoScan called with a null callback");
+            return false;
+        }
+
         try {
             boolean success = scannerImpl.startPnoScan(pnoSettings);
             if (success) {
-                callback.onPnoRequestSucceeded();
+                executor.execute(callback::onPnoRequestSucceeded);
             } else {
-                callback.onPnoRequestFailed();
+                executor.execute(callback::onPnoRequestFailed);
             }
             return success;
         } catch (RemoteException e1) {
@@ -820,8 +971,10 @@
     }
 
     /**
-     * Stop PNO scan.
-     * @param ifaceName Name of the interface.
+     * Stop PNO scan configured with
+     * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}.
+     *
+     * @param ifaceName Name of the interface on which the PNO scan was configured.
      * @return true on success.
      */
     public boolean stopPnoScan(@NonNull String ifaceName) {
@@ -839,8 +992,9 @@
     }
 
     /**
-     * Abort ongoing single scan.
-     * @param ifaceName Name of the interface.
+     * Abort ongoing single scan started with {@link #startScan(String, int, Set, List)}.
+     *
+     * @param ifaceName Name of the interface on which the scan was started.
      */
     public void abortScan(@NonNull String ifaceName) {
         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
@@ -856,7 +1010,7 @@
     }
 
     /**
-     * Query the list of valid frequencies for the provided band.
+     * Query the list of valid frequencies (in MHz) for the provided band.
      * The result depends on the on the country code that has been set.
      *
      * @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
@@ -865,31 +1019,39 @@
      * WifiScanner.WIFI_BAND_5_GHZ
      * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
      * WifiScanner.WIFI_BAND_6_GHZ
-     * @return frequencies vector of valid frequencies (MHz), or null for error.
+     * @return frequencies vector of valid frequencies (MHz), or an empty array for error.
      * @throws IllegalArgumentException if band is not recognized.
      */
-    public int [] getChannelsForBand(@WifiAnnotations.WifiBandBasic int band) {
+    public @NonNull int[] getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
         if (mWificond == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
-            return null;
+            return new int[0];
         }
+        int[] result = null;
         try {
             switch (band) {
                 case WifiScanner.WIFI_BAND_24_GHZ:
-                    return mWificond.getAvailable2gChannels();
+                    result = mWificond.getAvailable2gChannels();
+                    break;
                 case WifiScanner.WIFI_BAND_5_GHZ:
-                    return mWificond.getAvailable5gNonDFSChannels();
+                    result = mWificond.getAvailable5gNonDFSChannels();
+                    break;
                 case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
-                    return mWificond.getAvailableDFSChannels();
+                    result = mWificond.getAvailableDFSChannels();
+                    break;
                 case WifiScanner.WIFI_BAND_6_GHZ:
-                    return mWificond.getAvailable6gChannels();
+                    result = mWificond.getAvailable6gChannels();
+                    break;
                 default:
                     throw new IllegalArgumentException("unsupported band " + band);
             }
         } catch (RemoteException e1) {
             Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
         }
-        return null;
+        if (result == null) {
+            result = new int[0];
+        }
+        return result;
     }
 
     /** Helper function to look up the interface handle using name */
@@ -898,22 +1060,33 @@
     }
 
     /**
-     * Register the provided listener for SoftAp events.
+     * Register the provided callback handler for SoftAp events. Note that the Soft AP itself is
+     * configured using {@link #setupInterfaceForSoftApMode(String)}.
      *
-     * @param ifaceName Name of the interface.
-     * @param listener Callback for AP events.
+     * @param ifaceName Name of the interface on which to register the callback.
+     * @param executor The Executor on which to execute the callbacks.
+     * @param callback Callback for AP events.
      * @return true on success, false otherwise.
      */
-    public boolean registerApListener(@NonNull String ifaceName, SoftApListener listener) {
+    public boolean registerApCallback(@NonNull String ifaceName,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SoftApCallback callback) {
         IApInterface iface = getApInterface(ifaceName);
         if (iface == null) {
             Log.e(TAG, "No valid ap interface handler");
             return false;
         }
+
+        if (callback == null || executor == null) {
+            Log.e(TAG, "registerApCallback called with a null callback");
+            return false;
+        }
+
         try {
-            IApInterfaceEventCallback  callback = new ApInterfaceEventCallback(listener);
-            mApInterfaceListeners.put(ifaceName, callback);
-            boolean success = iface.registerCallback(callback);
+            IApInterfaceEventCallback wificondCallback = new ApInterfaceEventCallback(executor,
+                    callback);
+            mApInterfaceListeners.put(ifaceName, wificondCallback);
+            boolean success = iface.registerCallback(wificondCallback);
             if (!success) {
                 Log.e(TAG, "Failed to register ap callback.");
                 return false;
@@ -926,19 +1099,28 @@
     }
 
     /**
-     * See {@link #sendMgmtFrame(String, byte[], SendMgmtFrameCallback, int)}
+     * Send a management frame on the specified interface at the specified rate. Useful for probing
+     * the link with arbitrary frames.
+     *
+     * @param ifaceName The interface on which to send the frame.
+     * @param frame The raw byte array of the management frame to tramit.
+     * @param mcs The MCS (modulation and coding scheme), i.e. rate, at which to transmit the
+     *            frame. Specified per IEEE 802.11.
+     * @param executor The Executor on which to execute the callbacks.
+     * @param callback A {@link SendMgmtFrameCallback} callback for results of the operation.
      */
-    public void sendMgmtFrame(@NonNull String ifaceName, @NonNull byte[] frame,
-            @NonNull SendMgmtFrameCallback callback, int mcs) {
+    public void sendMgmtFrame(@NonNull String ifaceName, @NonNull byte[] frame, int mcs,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SendMgmtFrameCallback callback) {
 
-        if (callback == null) {
+        if (callback == null || executor == null) {
             Log.e(TAG, "callback cannot be null!");
             return;
         }
 
         if (frame == null) {
             Log.e(TAG, "frame cannot be null!");
-            callback.onFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN);
+            executor.execute(() -> callback.onFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN));
             return;
         }
 
@@ -946,17 +1128,17 @@
         IClientInterface clientInterface = getClientInterface(ifaceName);
         if (clientInterface == null) {
             Log.e(TAG, "No valid wificond client interface handler");
-            callback.onFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN);
+            executor.execute(() -> callback.onFailure(SEND_MGMT_FRAME_ERROR_UNKNOWN));
             return;
         }
 
         if (!mSendMgmtFrameInProgress.compareAndSet(false, true)) {
             Log.e(TAG, "An existing management frame transmission is in progress!");
-            callback.onFailure(SEND_MGMT_FRAME_ERROR_ALREADY_STARTED);
+            executor.execute(() -> callback.onFailure(SEND_MGMT_FRAME_ERROR_ALREADY_STARTED));
             return;
         }
 
-        SendMgmtFrameEvent sendMgmtFrameEvent = new SendMgmtFrameEvent(callback);
+        SendMgmtFrameEvent sendMgmtFrameEvent = new SendMgmtFrameEvent(executor, callback);
         try {
             clientInterface.SendMgmtFrame(frame, sendMgmtFrameEvent, mcs);
         } catch (RemoteException e) {
diff --git a/wifi/tests/src/android/net/wifi/wificond/PnoSettingsTest.java b/wifi/tests/src/android/net/wifi/wificond/PnoSettingsTest.java
index 775acc7..9439c79 100644
--- a/wifi/tests/src/android/net/wifi/wificond/PnoSettingsTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/PnoSettingsTest.java
@@ -30,7 +30,7 @@
 import java.util.HashMap;
 
 /**
- * Unit tests for {@link android.net.wifi.wificond.PnoSettingsResult}.
+ * Unit tests for {@link android.net.wifi.wificond.PnoSettings}.
  */
 @SmallTest
 public class PnoSettingsTest {
@@ -52,14 +52,14 @@
     @Before
     public void setUp() {
         mPnoNetwork1 = new PnoNetwork();
-        mPnoNetwork1.ssid = TEST_SSID_1;
-        mPnoNetwork1.isHidden = true;
-        mPnoNetwork1.frequencies = TEST_FREQUENCIES_1;
+        mPnoNetwork1.setSsid(TEST_SSID_1);
+        mPnoNetwork1.setHidden(true);
+        mPnoNetwork1.setFrequenciesMhz(TEST_FREQUENCIES_1);
 
         mPnoNetwork2 = new PnoNetwork();
-        mPnoNetwork2.ssid = TEST_SSID_2;
-        mPnoNetwork2.isHidden = false;
-        mPnoNetwork2.frequencies = TEST_FREQUENCIES_2;
+        mPnoNetwork2.setSsid(TEST_SSID_2);
+        mPnoNetwork2.setHidden(false);
+        mPnoNetwork2.setFrequenciesMhz(TEST_FREQUENCIES_2);
     }
 
     /**
@@ -69,10 +69,10 @@
     @Test
     public void canSerializeAndDeserialize() {
         PnoSettings pnoSettings = new PnoSettings();
-        pnoSettings.intervalMs = TEST_INTERVAL_MS;
-        pnoSettings.min2gRssi = TEST_MIN_2G_RSSI;
-        pnoSettings.min5gRssi = TEST_MIN_5G_RSSI;
-        pnoSettings.pnoNetworks = new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2));
+        pnoSettings.setIntervalMillis(TEST_INTERVAL_MS);
+        pnoSettings.setMin2gRssiDbm(TEST_MIN_2G_RSSI);
+        pnoSettings.setMin5gRssiDbm(TEST_MIN_5G_RSSI);
+        pnoSettings.setPnoNetworks(new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2)));
 
         Parcel parcel = Parcel.obtain();
         pnoSettings.writeToParcel(parcel, 0);
@@ -90,16 +90,16 @@
     @Test
     public void testAsHashMapKey() {
         PnoSettings pnoSettings1 = new PnoSettings();
-        pnoSettings1.intervalMs = TEST_INTERVAL_MS;
-        pnoSettings1.min2gRssi = TEST_MIN_2G_RSSI;
-        pnoSettings1.min5gRssi = TEST_MIN_5G_RSSI;
-        pnoSettings1.pnoNetworks = new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2));
+        pnoSettings1.setIntervalMillis(TEST_INTERVAL_MS);
+        pnoSettings1.setMin2gRssiDbm(TEST_MIN_2G_RSSI);
+        pnoSettings1.setMin5gRssiDbm(TEST_MIN_5G_RSSI);
+        pnoSettings1.setPnoNetworks(new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2)));
 
         PnoSettings pnoSettings2 = new PnoSettings();
-        pnoSettings2.intervalMs = TEST_INTERVAL_MS;
-        pnoSettings2.min2gRssi = TEST_MIN_2G_RSSI;
-        pnoSettings2.min5gRssi = TEST_MIN_5G_RSSI;
-        pnoSettings2.pnoNetworks = new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2));
+        pnoSettings2.setIntervalMillis(TEST_INTERVAL_MS);
+        pnoSettings2.setMin2gRssiDbm(TEST_MIN_2G_RSSI);
+        pnoSettings2.setMin5gRssiDbm(TEST_MIN_5G_RSSI);
+        pnoSettings2.setPnoNetworks(new ArrayList<>(Arrays.asList(mPnoNetwork1, mPnoNetwork2)));
 
         assertEquals(pnoSettings1, pnoSettings2);
         assertEquals(pnoSettings1.hashCode(), pnoSettings2.hashCode());
diff --git a/wifi/tests/src/android/net/wifi/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
similarity index 89%
rename from wifi/tests/src/android/net/wifi/WifiCondManagerTest.java
rename to wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index 48a9afa..68e5336 100644
--- a/wifi/tests/src/android/net/wifi/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.net.wifi.wificond;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -38,13 +38,18 @@
 import android.app.AlarmManager;
 import android.app.test.TestAlarmManager;
 import android.content.Context;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IApInterfaceEventCallback;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.IPnoScanEvent;
+import android.net.wifi.IScanEvent;
+import android.net.wifi.ISendMgmtFrameEvent;
+import android.net.wifi.IWifiScannerImpl;
+import android.net.wifi.IWificond;
+import android.net.wifi.SoftApInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.util.HexEncoding;
-import android.net.wifi.wificond.ChannelSettings;
-import android.net.wifi.wificond.HiddenNetwork;
-import android.net.wifi.wificond.NativeWifiClient;
-import android.net.wifi.wificond.PnoNetwork;
-import android.net.wifi.wificond.PnoSettings;
-import android.net.wifi.wificond.SingleScanSettings;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -67,7 +72,6 @@
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -88,7 +92,7 @@
     @Mock
     private IApInterface mApInterface;
     @Mock
-    private WifiCondManager.SoftApListener mSoftApListener;
+    private WifiCondManager.SoftApCallback mSoftApListener;
     @Mock
     private WifiCondManager.SendMgmtFrameCallback mSendMgmtFrameCallback;
     @Mock
@@ -122,6 +126,7 @@
     private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\"";
     private static final int[] TEST_FREQUENCIES_1 = {};
     private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
+    private static final byte[] TEST_RAW_MAC_BYTES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
 
     private static final List<byte[]> SCAN_HIDDEN_NETWORK_SSID_LIST =
             new ArrayList<byte[]>() {{
@@ -131,22 +136,23 @@
                         LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2)));
             }};
 
-    private static final PnoSettings TEST_PNO_SETTINGS =
-            new PnoSettings() {{
-                intervalMs = 6000;
-                pnoNetworks = new ArrayList<>();
-                PnoNetwork network = new PnoNetwork();
-                network.ssid = LocalNativeUtil.byteArrayFromArrayList(
-                        LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_1));
-                network.isHidden = true;
-                network.frequencies = TEST_FREQUENCIES_1;
-                pnoNetworks.add(network);
-                network.ssid = LocalNativeUtil.byteArrayFromArrayList(
-                        LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2));
-                network.isHidden = false;
-                network.frequencies = TEST_FREQUENCIES_2;
-                pnoNetworks.add(network);
-            }};
+    private static final PnoSettings TEST_PNO_SETTINGS = new PnoSettings();
+    static {
+        TEST_PNO_SETTINGS.setIntervalMillis(6000);
+        List<PnoNetwork> initPnoNetworks = new ArrayList<>();
+        PnoNetwork network = new PnoNetwork();
+        network.setSsid(LocalNativeUtil.byteArrayFromArrayList(
+                LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_1)));
+        network.setHidden(true);
+        network.setFrequenciesMhz(TEST_FREQUENCIES_1);
+        initPnoNetworks.add(network);
+        network.setSsid(LocalNativeUtil.byteArrayFromArrayList(
+                LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2)));
+        network.setHidden(false);
+        network.setFrequenciesMhz(TEST_FREQUENCIES_2);
+        initPnoNetworks.add(network);
+        TEST_PNO_SETTINGS.setPnoNetworks(initPnoNetworks);
+    }
 
     private static final int TEST_MCS_RATE = 5;
     private static final int TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS = 100;
@@ -180,8 +186,9 @@
         when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl);
         when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
         mWificondControl = new WifiCondManager(mContext, mWificond);
-        assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME,
-                mNormalScanCallback, mPnoScanCallback));
+        assertEquals(true,
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run,
+                        mNormalScanCallback, mPnoScanCallback));
     }
 
     /**
@@ -264,7 +271,7 @@
         assertNull(mWificondControl.signalPoll(TEST_INTERFACE_NAME));
         verify(mClientInterface, never()).signalPoll();
 
-        assertFalse(mWificondControl.scan(
+        assertFalse(mWificondControl.startScan(
                 TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_LATENCY,
                 SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST));
         verify(mWifiScannerImpl, never()).scan(any());
@@ -348,8 +355,8 @@
     public void testTeardownSoftApInterfaceClearsHandles() throws Exception {
         testTeardownSoftApInterface();
 
-        assertFalse(mWificondControl.registerApListener(
-                TEST_INTERFACE_NAME, mSoftApListener));
+        assertFalse(mWificondControl.registerApCallback(
+                TEST_INTERFACE_NAME, Runnable::run, mSoftApListener));
         verify(mApInterface, never()).registerCallback(any());
     }
 
@@ -417,8 +424,8 @@
     public void testSignalPoll() throws Exception {
         when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
 
-        mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, mNormalScanCallback,
-                mPnoScanCallback);
+        mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run,
+                mNormalScanCallback, mPnoScanCallback);
         mWificondControl.signalPoll(TEST_INTERFACE_NAME);
         verify(mClientInterface).signalPoll();
     }
@@ -432,7 +439,7 @@
 
         // Configure client interface.
         assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME,
-                mNormalScanCallback, mPnoScanCallback));
+                Runnable::run, mNormalScanCallback, mPnoScanCallback));
 
         // Tear down interfaces.
         assertTrue(mWificondControl.tearDownInterfaces());
@@ -448,8 +455,8 @@
     public void testGetTxPacketCounters() throws Exception {
         when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
 
-        mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, mNormalScanCallback,
-                mPnoScanCallback);
+        mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run,
+                mNormalScanCallback, mPnoScanCallback);
         mWificondControl.getTxPacketCounters(TEST_INTERFACE_NAME);
         verify(mClientInterface).getPacketCounters();
     }
@@ -464,7 +471,7 @@
 
         // Configure client interface.
         assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME,
-                mNormalScanCallback, mPnoScanCallback));
+                Runnable::run, mNormalScanCallback, mPnoScanCallback));
 
         // Tear down interfaces.
         assertTrue(mWificondControl.tearDownInterfaces());
@@ -483,7 +490,7 @@
 
         // Configure client interface.
         assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME,
-                mNormalScanCallback, mPnoScanCallback));
+                Runnable::run, mNormalScanCallback, mPnoScanCallback));
 
         // Tear down interfaces.
         assertTrue(mWificondControl.tearDownInterfaces());
@@ -500,7 +507,7 @@
     @Test
     public void testScan() throws Exception {
         when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
-        assertTrue(mWificondControl.scan(
+        assertTrue(mWificondControl.startScan(
                 TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER,
                 SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST));
         verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
@@ -520,7 +527,7 @@
         assertEquals(hiddenSsidWithDup.get(0),
                 hiddenSsidWithDup.get(hiddenSsidWithDup.size() - 1));
         // Pass the List with duplicate elements into scan()
-        assertTrue(mWificondControl.scan(
+        assertTrue(mWificondControl.startScan(
                 TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER,
                 SCAN_FREQ_SET, hiddenSsidWithDup));
         // But the argument passed down should have the duplicate removed.
@@ -535,7 +542,7 @@
     @Test
     public void testScanNullParameters() throws Exception {
         when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
-        assertTrue(mWificondControl.scan(
+        assertTrue(mWificondControl.startScan(
                 TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_HIGH_ACCURACY, null, null));
         verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
                 IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null)));
@@ -547,7 +554,7 @@
     @Test
     public void testScanFailure() throws Exception {
         when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(false);
-        assertFalse(mWificondControl.scan(
+        assertFalse(mWificondControl.startScan(
                 TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_LATENCY,
                 SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST));
         verify(mWifiScannerImpl).scan(any(SingleScanSettings.class));
@@ -558,7 +565,7 @@
      */
     @Test
     public void testScanFailureDueToInvalidType() throws Exception {
-        assertFalse(mWificondControl.scan(
+        assertFalse(mWificondControl.startScan(
                 TEST_INTERFACE_NAME, 100,
                 SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST));
         verify(mWifiScannerImpl, never()).scan(any(SingleScanSettings.class));
@@ -570,9 +577,10 @@
     @Test
     public void testStartPnoScan() throws Exception {
         when(mWifiScannerImpl.startPnoScan(any(PnoSettings.class))).thenReturn(true);
-        assertTrue(mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS,
-                mPnoScanRequestCallback));
-        verify(mWifiScannerImpl).startPnoScan(argThat(new PnoScanMatcher(TEST_PNO_SETTINGS)));
+        assertTrue(
+                mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS, Runnable::run,
+                        mPnoScanRequestCallback));
+        verify(mWifiScannerImpl).startPnoScan(eq(TEST_PNO_SETTINGS));
         verify(mPnoScanRequestCallback).onPnoRequestSucceeded();
     }
 
@@ -665,8 +673,9 @@
     public void testStartPnoScanForMetrics() throws Exception {
         when(mWifiScannerImpl.startPnoScan(any(PnoSettings.class))).thenReturn(false);
 
-        assertFalse(mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS,
-                mPnoScanRequestCallback));
+        assertFalse(
+                mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS, Runnable::run,
+                        mPnoScanRequestCallback));
         verify(mPnoScanRequestCallback).onPnoRequestFailed();
     }
 
@@ -695,11 +704,11 @@
         final ArgumentCaptor<IApInterfaceEventCallback> apInterfaceCallbackCaptor =
                 ArgumentCaptor.forClass(IApInterfaceEventCallback.class);
 
-        assertTrue(mWificondControl.registerApListener(
-                TEST_INTERFACE_NAME, mSoftApListener));
+        assertTrue(mWificondControl.registerApCallback(
+                TEST_INTERFACE_NAME, Runnable::run, mSoftApListener));
         verify(mApInterface).registerCallback(apInterfaceCallbackCaptor.capture());
 
-        final NativeWifiClient testClient = new NativeWifiClient();
+        final NativeWifiClient testClient = new NativeWifiClient(TEST_RAW_MAC_BYTES);
         apInterfaceCallbackCaptor.getValue().onConnectedClientsChanged(testClient, true);
         verify(mSoftApListener).onConnectedClientsChanged(eq(testClient), eq(true));
 
@@ -707,7 +716,8 @@
         int channelBandwidth = IApInterfaceEventCallback.BANDWIDTH_20;
         apInterfaceCallbackCaptor.getValue().onSoftApChannelSwitched(channelFrequency,
                 channelBandwidth);
-        verify(mSoftApListener).onSoftApChannelSwitched(eq(channelFrequency), eq(channelBandwidth));
+        verify(mSoftApListener).onSoftApChannelSwitched(eq(channelFrequency),
+                eq(SoftApInfo.CHANNEL_WIDTH_20MHZ));
     }
 
     /**
@@ -739,7 +749,7 @@
         verify(deathHandler).run();
 
         // The handles should be cleared after death.
-        assertNull(mWificondControl.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ));
+        assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).length);
         verify(mWificond, never()).getAvailable5gNonDFSChannels();
     }
 
@@ -748,7 +758,8 @@
      */
     @Test
     public void testSendMgmtFrameNullCallback() throws Exception {
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, null, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, null);
 
         verify(mClientInterface, never()).SendMgmtFrame(any(), any(), anyInt());
     }
@@ -758,8 +769,8 @@
      */
     @Test
     public void testSendMgmtFrameNullFrame() throws Exception {
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, null,
-                mSendMgmtFrameCallback, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, null, TEST_MCS_RATE, Runnable::run,
+                mSendMgmtFrameCallback);
 
         verify(mClientInterface, never()).SendMgmtFrame(any(), any(), anyInt());
         verify(mSendMgmtFrameCallback).onFailure(anyInt());
@@ -770,8 +781,8 @@
      */
     @Test
     public void testSendMgmtFrameInvalidInterfaceName() throws Exception {
-        mWificondControl.sendMgmtFrame(TEST_INVALID_INTERFACE_NAME, TEST_PROBE_FRAME,
-                mSendMgmtFrameCallback, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INVALID_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, mSendMgmtFrameCallback);
 
         verify(mClientInterface, never()).SendMgmtFrame(any(), any(), anyInt());
         verify(mSendMgmtFrameCallback).onFailure(anyInt());
@@ -787,13 +798,15 @@
         WifiCondManager.SendMgmtFrameCallback cb2 = mock(
                 WifiCondManager.SendMgmtFrameCallback.class);
 
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, cb1, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, cb1);
         verify(cb1, never()).onFailure(anyInt());
         verify(mClientInterface, times(1))
                 .SendMgmtFrame(AdditionalMatchers.aryEq(TEST_PROBE_FRAME),
                         any(), eq(TEST_MCS_RATE));
 
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, cb2, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, cb2);
         verify(cb2).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED);
         // verify SendMgmtFrame() still was only called once i.e. not called again
         verify(mClientInterface, times(1))
@@ -820,8 +833,8 @@
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
                 alarmListenerCaptor.capture(), handlerCaptor.capture());
 
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME,
-                cb, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, cb);
         mLooper.dispatchAll();
 
         verify(cb).onFailure(anyInt());
@@ -854,7 +867,8 @@
         final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
                 alarmListenerCaptor.capture(), handlerCaptor.capture());
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, cb, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, cb);
 
         sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS);
         mLooper.dispatchAll();
@@ -887,7 +901,8 @@
         final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
                 alarmListenerCaptor.capture(), handlerCaptor.capture());
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, cb, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, cb);
 
         sendMgmtFrameEventCaptor.getValue().OnFailure(
                 WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
@@ -921,7 +936,8 @@
         final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
                 alarmListenerCaptor.capture(), handlerCaptor.capture());
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, cb, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, cb);
 
         handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
         mLooper.dispatchAll();
@@ -987,8 +1003,8 @@
         final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
                 alarmListenerCaptor.capture(), handlerCaptor.capture());
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME,
-                mSendMgmtFrameCallback, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, mSendMgmtFrameCallback);
 
         // AlarmManager should post the onAlarm() callback onto the handler, but since we are
         // triggering onAlarm() ourselves during the test, manually post onto handler
@@ -1015,8 +1031,8 @@
         final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
                 alarmListenerCaptor.capture(), handlerCaptor.capture());
-        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME,
-                mSendMgmtFrameCallback, TEST_MCS_RATE);
+        mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
+                Runnable::run, mSendMgmtFrameCallback);
 
         // AlarmManager should post the onAlarm() callback onto the handler, but since we are
         // triggering onAlarm() ourselves during the test, manually post onto handler
@@ -1086,56 +1102,6 @@
         }
     }
 
-    // Create a ArgumentMatcher which captures a PnoSettings parameter and checks if it
-    // matches the WifiNative.PnoSettings;
-    private class PnoScanMatcher implements ArgumentMatcher<PnoSettings> {
-        private final PnoSettings mExpectedPnoSettings;
-
-        PnoScanMatcher(PnoSettings expectedPnoSettings) {
-            this.mExpectedPnoSettings = expectedPnoSettings;
-        }
-
-        @Override
-        public boolean matches(PnoSettings settings) {
-            if (mExpectedPnoSettings == null) {
-                return false;
-            }
-            if (settings.intervalMs != mExpectedPnoSettings.intervalMs
-                    || settings.min2gRssi != mExpectedPnoSettings.min2gRssi
-                    || settings.min5gRssi != mExpectedPnoSettings.min5gRssi
-                    || settings.min6gRssi != mExpectedPnoSettings.min6gRssi) {
-                return false;
-            }
-            if (settings.pnoNetworks == null || mExpectedPnoSettings.pnoNetworks == null) {
-                return false;
-            }
-            if (settings.pnoNetworks.size() != mExpectedPnoSettings.pnoNetworks.size()) {
-                return false;
-            }
-
-            for (int i = 0; i < settings.pnoNetworks.size(); i++) {
-                if (!Arrays.equals(settings.pnoNetworks.get(i).ssid,
-                        mExpectedPnoSettings.pnoNetworks.get(i).ssid)) {
-                    return false;
-                }
-                if (settings.pnoNetworks.get(i).isHidden != mExpectedPnoSettings.pnoNetworks.get(
-                        i).isHidden) {
-                    return false;
-                }
-                if (!Arrays.equals(settings.pnoNetworks.get(i).frequencies,
-                        mExpectedPnoSettings.pnoNetworks.get(i).frequencies)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public String toString() {
-            return "PnoScanMatcher{" + "mExpectedPnoSettings=" + mExpectedPnoSettings + '}';
-        }
-    }
-
     private static class LocalNativeUtil {
         private static final int SSID_BYTES_MAX_LEN = 32;