Merge "Changed style in NotificationInfo to DeviceDefault"
diff --git a/Android.bp b/Android.bp
index c97ee3d..9077344 100644
--- a/Android.bp
+++ b/Android.bp
@@ -352,12 +352,8 @@
         "core/java/android/service/chooser/IChooserTargetResult.aidl",
         "core/java/android/service/resolver/IResolverRankerService.aidl",
         "core/java/android/service/resolver/IResolverRankerResult.aidl",
-        "core/java/android/service/textclassifier/IConversationActionsCallback.aidl",
-        "core/java/android/service/textclassifier/ITextClassificationCallback.aidl",
+        "core/java/android/service/textclassifier/ITextClassifierCallback.aidl",
         "core/java/android/service/textclassifier/ITextClassifierService.aidl",
-        "core/java/android/service/textclassifier/ITextLanguageCallback.aidl",
-        "core/java/android/service/textclassifier/ITextLinksCallback.aidl",
-        "core/java/android/service/textclassifier/ITextSelectionCallback.aidl",
         "core/java/android/service/attention/IAttentionService.aidl",
         "core/java/android/service/attention/IAttentionCallback.aidl",
         "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
diff --git a/api/current.txt b/api/current.txt
index 9974de4..3ba0dd2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4277,7 +4277,8 @@
     method public void stopWatchingMode(android.app.AppOpsManager.OnOpChangedListener);
     method public int unsafeCheckOp(String, int, String);
     method public int unsafeCheckOpNoThrow(String, int, String);
-    method public int unsafeCheckOpRaw(String, int, String);
+    method public int unsafeCheckOpRaw(@NonNull String, int, String);
+    method public int unsafeCheckOpRawNoThrow(@NonNull String, int, @NonNull String);
     field public static final int MODE_ALLOWED = 0; // 0x0
     field public static final int MODE_DEFAULT = 3; // 0x3
     field public static final int MODE_ERRORED = 2; // 0x2
@@ -4446,7 +4447,7 @@
 
   public final class AutomaticZenRule implements android.os.Parcelable {
     ctor @Deprecated public AutomaticZenRule(String, android.content.ComponentName, android.net.Uri, int, boolean);
-    ctor public AutomaticZenRule(String, android.content.ComponentName, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, int, boolean);
+    ctor public AutomaticZenRule(@NonNull String, @Nullable android.content.ComponentName, @Nullable android.content.ComponentName, @NonNull android.net.Uri, @Nullable android.service.notification.ZenPolicy, int, boolean);
     ctor public AutomaticZenRule(android.os.Parcel);
     method public int describeContents();
     method public android.net.Uri getConditionId();
@@ -10637,9 +10638,9 @@
   }
 
   public final class LocusId implements android.os.Parcelable {
-    ctor public LocusId(@NonNull android.net.Uri);
+    ctor public LocusId(@NonNull String);
     method public int describeContents();
-    method @NonNull public android.net.Uri getUri();
+    method @NonNull public String getId();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.LocusId> CREATOR;
   }
@@ -14155,6 +14156,7 @@
     method @Nullable public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener();
     method @Nullable public android.graphics.PostProcessor getPostProcessor();
     method public boolean isDecodeAsAlphaMaskEnabled();
+    method public static boolean isMimeTypeSupported(@NonNull String);
     method public boolean isMutableRequired();
     method public boolean isUnpremultipliedRequired();
     method public void setAllocator(int);
@@ -16796,6 +16798,7 @@
 
   public abstract static class CameraManager.AvailabilityCallback {
     ctor public CameraManager.AvailabilityCallback();
+    method public void onCameraAccessPrioritiesChanged();
     method public void onCameraAvailable(@NonNull String);
     method public void onCameraUnavailable(@NonNull String);
   }
@@ -22856,7 +22859,6 @@
     method public float getBearing();
     method public float getBearingAccuracyDegrees();
     method public long getElapsedRealtimeNanos();
-    method public long getElapsedRealtimeUncertaintyNanos();
     method public android.os.Bundle getExtras();
     method public double getLatitude();
     method public double getLongitude();
@@ -22869,7 +22871,6 @@
     method public boolean hasAltitude();
     method public boolean hasBearing();
     method public boolean hasBearingAccuracy();
-    method public boolean hasElapsedRealtimeUncertaintyNanos();
     method public boolean hasSpeed();
     method public boolean hasSpeedAccuracy();
     method public boolean hasVerticalAccuracy();
@@ -22885,7 +22886,6 @@
     method public void setBearing(float);
     method public void setBearingAccuracyDegrees(float);
     method public void setElapsedRealtimeNanos(long);
-    method public void setElapsedRealtimeUncertaintyNanos(long);
     method public void setExtras(android.os.Bundle);
     method public void setLatitude(double);
     method public void setLongitude(double);
@@ -23053,7 +23053,7 @@
     ctor public AudioAttributes.Builder();
     ctor public AudioAttributes.Builder(android.media.AudioAttributes);
     method public android.media.AudioAttributes build();
-    method public android.media.AudioAttributes.Builder setAllowCapture(boolean);
+    method @NonNull public android.media.AudioAttributes.Builder setAllowCapture(boolean);
     method public android.media.AudioAttributes.Builder setContentType(int);
     method public android.media.AudioAttributes.Builder setFlags(int);
     method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
@@ -23392,11 +23392,11 @@
 
   public static final class AudioPlaybackCaptureConfiguration.Builder {
     ctor public AudioPlaybackCaptureConfiguration.Builder(@NonNull android.media.projection.MediaProjection);
-    method public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUid(int);
-    method public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUsage(@NonNull android.media.AudioAttributes);
-    method public android.media.AudioPlaybackCaptureConfiguration build();
-    method public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUid(int);
-    method public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUsage(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUid(int);
+    method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUsage(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.AudioPlaybackCaptureConfiguration build();
+    method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUid(int);
+    method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUsage(@NonNull android.media.AudioAttributes);
   }
 
   public final class AudioPlaybackConfiguration implements android.os.Parcelable {
@@ -23497,7 +23497,7 @@
     ctor public AudioRecord.Builder();
     method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
     method public android.media.AudioRecord.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
-    method public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
+    method @NonNull public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
     method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
   }
@@ -23569,7 +23569,6 @@
     method @Deprecated public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration);
-    method @Deprecated public static void deprecateStreamTypeForPlayback(int, @NonNull String, @NonNull String) throws java.lang.IllegalArgumentException;
     method protected void finalize();
     method public void flush();
     method @NonNull public android.media.AudioAttributes getAudioAttributes();
@@ -23689,18 +23688,18 @@
 
   public abstract static class AudioTrack.StreamEventCallback {
     ctor public AudioTrack.StreamEventCallback();
-    method public void onDataRequest(@NonNull android.media.AudioTrack, int);
+    method public void onDataRequest(@NonNull android.media.AudioTrack, @IntRange(from=0) int);
     method public void onPresentationEnded(@NonNull android.media.AudioTrack);
     method public void onTearDown(@NonNull android.media.AudioTrack);
   }
 
   public class CallbackDataSourceDesc extends android.media.DataSourceDesc {
-    method public android.media.DataSourceCallback getDataSourceCallback();
+    method @NonNull public android.media.DataSourceCallback getDataSourceCallback();
   }
 
   public static class CallbackDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase<android.media.CallbackDataSourceDesc.Builder> {
     ctor public CallbackDataSourceDesc.Builder();
-    ctor public CallbackDataSourceDesc.Builder(android.media.CallbackDataSourceDesc);
+    ctor public CallbackDataSourceDesc.Builder(@Nullable android.media.CallbackDataSourceDesc);
     method @NonNull public android.media.CallbackDataSourceDesc build();
     method @NonNull public android.media.CallbackDataSourceDesc.Builder setDataSource(@NonNull android.media.DataSourceCallback);
   }
@@ -23760,12 +23759,12 @@
   public abstract class DataSourceCallback implements java.io.Closeable {
     ctor public DataSourceCallback();
     method public abstract long getSize() throws java.io.IOException;
-    method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
+    method public abstract int readAt(long, @NonNull byte[], int, int) throws java.io.IOException;
   }
 
   public class DataSourceDesc {
     method public long getEndPosition();
-    method public String getMediaId();
+    method @Nullable public String getMediaId();
     method public long getStartPosition();
     field public static final long LONG_MAX_TIME_MS = 576460752303423L; // 0x20c49ba5e353fL
     field public static final long POSITION_UNKNOWN = 576460752303423L; // 0x20c49ba5e353fL
@@ -23773,7 +23772,7 @@
 
   protected static class DataSourceDesc.BuilderBase<T extends android.media.DataSourceDesc.BuilderBase> {
     method @NonNull public T setEndPosition(long);
-    method @NonNull public T setMediaId(String);
+    method @NonNull public T setMediaId(@Nullable String);
     method @NonNull public T setStartPosition(long);
   }
 
@@ -23982,13 +23981,13 @@
   public class FileDataSourceDesc extends android.media.DataSourceDesc {
     method public long getLength();
     method public long getOffset();
-    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+    method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor();
     field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
   }
 
   public static class FileDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase<android.media.FileDataSourceDesc.Builder> {
     ctor public FileDataSourceDesc.Builder();
-    ctor public FileDataSourceDesc.Builder(android.media.FileDataSourceDesc);
+    ctor public FileDataSourceDesc.Builder(@Nullable android.media.FileDataSourceDesc);
     method @NonNull public android.media.FileDataSourceDesc build();
     method @NonNull public android.media.FileDataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.media.FileDataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor, long, long);
@@ -25138,7 +25137,7 @@
     field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
   }
 
-  public static class MediaItem2.Builder {
+  public static final class MediaItem2.Builder {
     ctor public MediaItem2.Builder();
     method @NonNull public android.media.MediaItem2 build();
     method @NonNull public android.media.MediaItem2.Builder setEndPosition(long);
@@ -25326,7 +25325,6 @@
     method public static android.media.MediaPlayer create(android.content.Context, int);
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration);
-    method @Deprecated public static void deprecateStreamTypeForPlayback(int, @NonNull String, @NonNull String) throws java.lang.IllegalArgumentException;
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method protected void finalize();
     method public int getAudioSessionId();
@@ -25540,16 +25538,16 @@
   }
 
   public class MediaPlayer2 implements android.media.AudioRouting java.lang.AutoCloseable {
-    ctor public MediaPlayer2(android.content.Context);
-    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
-    method public Object attachAuxEffect(int);
+    ctor public MediaPlayer2(@NonNull android.content.Context);
+    method public void addOnRoutingChangedListener(@NonNull android.media.AudioRouting.OnRoutingChangedListener, @Nullable android.os.Handler);
+    method @NonNull public Object attachAuxEffect(int);
     method public boolean cancelCommand(@NonNull Object);
     method public void clearDrmEventCallback();
-    method public Object clearNextDataSources();
+    method @NonNull public Object clearNextDataSources();
     method public void clearPendingCommands();
     method public void close();
-    method public Object deselectTrack(int);
-    method public Object deselectTrack(@NonNull android.media.DataSourceDesc, int);
+    method @NonNull public Object deselectTrack(int);
+    method @NonNull public Object deselectTrack(@NonNull android.media.DataSourceDesc, int);
     method @NonNull public android.media.AudioAttributes getAudioAttributes();
     method public int getAudioSessionId();
     method public long getBufferedPosition();
@@ -25559,11 +25557,11 @@
     method public long getDuration();
     method public long getDuration(@NonNull android.media.DataSourceDesc);
     method public float getMaxPlayerVolume();
-    method public android.os.PersistableBundle getMetrics();
+    method @Nullable public android.os.PersistableBundle getMetrics();
     method @NonNull public android.media.PlaybackParams getPlaybackParams();
     method public float getPlayerVolume();
-    method public android.media.AudioDeviceInfo getPreferredDevice();
-    method public android.media.AudioDeviceInfo getRoutedDevice();
+    method @Nullable public android.media.AudioDeviceInfo getPreferredDevice();
+    method @Nullable public android.media.AudioDeviceInfo getRoutedDevice();
     method public int getSelectedTrack(int);
     method public int getSelectedTrack(@NonNull android.media.DataSourceDesc, int);
     method public int getState();
@@ -25571,37 +25569,37 @@
     method @Nullable public android.media.MediaTimestamp getTimestamp();
     method @NonNull public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
     method @NonNull public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(@NonNull android.media.DataSourceDesc);
-    method public android.util.Size getVideoSize();
+    method @NonNull public android.util.Size getVideoSize();
     method public boolean isLooping();
-    method public Object loopCurrent(boolean);
-    method public Object notifyWhenCommandLabelReached(@NonNull Object);
-    method public Object pause();
-    method public Object play();
-    method public Object prepare();
+    method @NonNull public Object loopCurrent(boolean);
+    method @NonNull public Object notifyWhenCommandLabelReached(@NonNull Object);
+    method @NonNull public Object pause();
+    method @NonNull public Object play();
+    method @NonNull public Object prepare();
     method public void registerEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer2.EventCallback);
-    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public void removeOnRoutingChangedListener(@NonNull android.media.AudioRouting.OnRoutingChangedListener);
     method public void reset();
-    method public Object seekTo(long);
-    method public Object seekTo(long, int);
-    method public Object selectTrack(int);
-    method public Object selectTrack(@NonNull android.media.DataSourceDesc, int);
-    method public Object setAudioAttributes(@NonNull android.media.AudioAttributes);
-    method public Object setAudioSessionId(int);
-    method public Object setAuxEffectSendLevel(float);
-    method public Object setDataSource(@NonNull android.media.DataSourceDesc);
-    method public Object setDisplay(android.view.SurfaceHolder);
+    method @NonNull public Object seekTo(long);
+    method @NonNull public Object seekTo(long, int);
+    method @NonNull public Object selectTrack(int);
+    method @NonNull public Object selectTrack(@NonNull android.media.DataSourceDesc, int);
+    method @NonNull public Object setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public Object setAudioSessionId(int);
+    method @NonNull public Object setAuxEffectSendLevel(float);
+    method @NonNull public Object setDataSource(@NonNull android.media.DataSourceDesc);
+    method @NonNull public Object setDisplay(@Nullable android.view.SurfaceHolder);
     method public void setDrmEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer2.DrmEventCallback);
-    method public Object setNextDataSource(@NonNull android.media.DataSourceDesc);
-    method public Object setNextDataSources(@NonNull java.util.List<android.media.DataSourceDesc>);
-    method public Object setPlaybackParams(@NonNull android.media.PlaybackParams);
-    method public Object setPlayerVolume(float);
-    method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
-    method public Object setScreenOnWhilePlaying(boolean);
-    method public Object setSurface(android.view.Surface);
-    method public Object setSyncParams(@NonNull android.media.SyncParams);
-    method public Object setWakeLock(@NonNull android.os.PowerManager.WakeLock);
-    method public Object skipToNext();
-    method public void unregisterEventCallback(android.media.MediaPlayer2.EventCallback);
+    method @NonNull public Object setNextDataSource(@NonNull android.media.DataSourceDesc);
+    method @NonNull public Object setNextDataSources(@NonNull java.util.List<android.media.DataSourceDesc>);
+    method @NonNull public Object setPlaybackParams(@NonNull android.media.PlaybackParams);
+    method @NonNull public Object setPlayerVolume(float);
+    method public boolean setPreferredDevice(@Nullable android.media.AudioDeviceInfo);
+    method @NonNull public Object setScreenOnWhilePlaying(boolean);
+    method @NonNull public Object setSurface(@Nullable android.view.Surface);
+    method @NonNull public Object setSyncParams(@NonNull android.media.SyncParams);
+    method @NonNull public Object setWakeLock(@NonNull android.os.PowerManager.WakeLock);
+    method @NonNull public Object skipToNext();
+    method public void unregisterEventCallback(@NonNull android.media.MediaPlayer2.EventCallback);
     field public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1; // 0x1
     field public static final int CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES = 30; // 0x1e
     field public static final int CALL_COMPLETED_DESELECT_TRACK = 2; // 0x2
@@ -25736,8 +25734,8 @@
   }
 
   public static class MediaPlayer2.TrackInfo {
-    method public android.media.MediaFormat getFormat();
-    method public String getLanguage();
+    method @Nullable public android.media.MediaFormat getFormat();
+    method @Nullable public String getLanguage();
     method public int getTrackType();
     field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
     field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
@@ -26421,7 +26419,6 @@
     ctor @Deprecated public SoundPool(int, int, int);
     method public final void autoPause();
     method public final void autoResume();
-    method @Deprecated public static void deprecateStreamTypeForPlayback(int, @NonNull String, @NonNull String) throws java.lang.IllegalArgumentException;
     method protected void finalize();
     method public int load(String, int);
     method public int load(android.content.Context, int, int);
@@ -26617,15 +26614,15 @@
   }
 
   public class UriDataSourceDesc extends android.media.DataSourceDesc {
-    method public android.content.Context getContext();
-    method public java.util.List<java.net.HttpCookie> getCookies();
-    method public java.util.Map<java.lang.String,java.lang.String> getHeaders();
-    method public android.net.Uri getUri();
+    method @NonNull public android.content.Context getContext();
+    method @Nullable public java.util.List<java.net.HttpCookie> getCookies();
+    method @Nullable public java.util.Map<java.lang.String,java.lang.String> getHeaders();
+    method @NonNull public android.net.Uri getUri();
   }
 
   public static class UriDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase<android.media.UriDataSourceDesc.Builder> {
     ctor public UriDataSourceDesc.Builder();
-    ctor public UriDataSourceDesc.Builder(android.media.UriDataSourceDesc);
+    ctor public UriDataSourceDesc.Builder(@Nullable android.media.UriDataSourceDesc);
     method @NonNull public android.media.UriDataSourceDesc build();
     method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri);
     method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable java.util.Map<java.lang.String,java.lang.String>, @Nullable java.util.List<java.net.HttpCookie>);
@@ -28980,6 +28977,7 @@
     field public static final int NET_CAPABILITY_IA = 7; // 0x7
     field public static final int NET_CAPABILITY_IMS = 4; // 0x4
     field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
+    field public static final int NET_CAPABILITY_MCX = 23; // 0x17
     field public static final int NET_CAPABILITY_MMS = 0; // 0x0
     field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
     field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
@@ -38419,7 +38417,8 @@
     method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri);
     method public static android.net.Uri getMediaScannerUri();
     method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
-    method public static String getVersion(android.content.Context);
+    method @NonNull public static String getVersion(@NonNull android.content.Context);
+    method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
     method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
     method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
     method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
@@ -41870,26 +41869,26 @@
 
   public static class ZenPolicy.Builder {
     ctor public ZenPolicy.Builder();
-    method public android.service.notification.ZenPolicy.Builder allowAlarms(boolean);
-    method public android.service.notification.ZenPolicy.Builder allowAllSounds();
-    method public android.service.notification.ZenPolicy.Builder allowCalls(int);
-    method public android.service.notification.ZenPolicy.Builder allowEvents(boolean);
-    method public android.service.notification.ZenPolicy.Builder allowMedia(boolean);
-    method public android.service.notification.ZenPolicy.Builder allowMessages(int);
-    method public android.service.notification.ZenPolicy.Builder allowReminders(boolean);
-    method public android.service.notification.ZenPolicy.Builder allowRepeatCallers(boolean);
-    method public android.service.notification.ZenPolicy.Builder allowSystem(boolean);
-    method public android.service.notification.ZenPolicy build();
-    method public android.service.notification.ZenPolicy.Builder disallowAllSounds();
-    method public android.service.notification.ZenPolicy.Builder hideAllVisualEffects();
-    method public android.service.notification.ZenPolicy.Builder showAllVisualEffects();
-    method public android.service.notification.ZenPolicy.Builder showBadges(boolean);
-    method public android.service.notification.ZenPolicy.Builder showFullScreenIntent(boolean);
-    method public android.service.notification.ZenPolicy.Builder showInAmbientDisplay(boolean);
-    method public android.service.notification.ZenPolicy.Builder showInNotificationList(boolean);
-    method public android.service.notification.ZenPolicy.Builder showLights(boolean);
-    method public android.service.notification.ZenPolicy.Builder showPeeking(boolean);
-    method public android.service.notification.ZenPolicy.Builder showStatusBarIcons(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowAlarms(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowAllSounds();
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowCalls(int);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowEvents(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowMedia(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowMessages(int);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowReminders(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowRepeatCallers(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowSystem(boolean);
+    method @NonNull public android.service.notification.ZenPolicy build();
+    method @NonNull public android.service.notification.ZenPolicy.Builder disallowAllSounds();
+    method @NonNull public android.service.notification.ZenPolicy.Builder hideAllVisualEffects();
+    method @NonNull public android.service.notification.ZenPolicy.Builder showAllVisualEffects();
+    method @NonNull public android.service.notification.ZenPolicy.Builder showBadges(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder showFullScreenIntent(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder showInAmbientDisplay(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder showInNotificationList(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder showLights(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder showPeeking(boolean);
+    method @NonNull public android.service.notification.ZenPolicy.Builder showStatusBarIcons(boolean);
   }
 
 }
@@ -43355,14 +43354,14 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallIdentification> CREATOR;
   }
 
-  public static class CallIdentification.Builder {
+  public static final class CallIdentification.Builder {
     ctor public CallIdentification.Builder();
-    method public android.telecom.CallIdentification build();
-    method public android.telecom.CallIdentification.Builder setDescription(@Nullable CharSequence);
-    method public android.telecom.CallIdentification.Builder setDetails(@Nullable CharSequence);
-    method public android.telecom.CallIdentification.Builder setName(@Nullable CharSequence);
-    method public android.telecom.CallIdentification.Builder setNuisanceConfidence(int);
-    method public android.telecom.CallIdentification.Builder setPhoto(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.telecom.CallIdentification build();
+    method @NonNull public android.telecom.CallIdentification.Builder setDescription(@Nullable CharSequence);
+    method @NonNull public android.telecom.CallIdentification.Builder setDetails(@Nullable CharSequence);
+    method @NonNull public android.telecom.CallIdentification.Builder setName(@Nullable CharSequence);
+    method @NonNull public android.telecom.CallIdentification.Builder setNuisanceConfidence(int);
+    method @NonNull public android.telecom.CallIdentification.Builder setPhoto(@Nullable android.graphics.drawable.Icon);
   }
 
   public abstract class CallRedirectionService extends android.app.Service {
@@ -43983,8 +43982,8 @@
     method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
     method public android.telecom.PhoneAccountHandle getSimCallManager();
-    method public String getSystemDialerPackage();
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
+    method @Nullable public String getSystemDialerPackage();
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public android.telecom.PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
@@ -45145,8 +45144,8 @@
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei();
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
-    method public String getManufacturerCode();
-    method public String getManufacturerCode(int);
+    method @Nullable public String getManufacturerCode();
+    method @Nullable public String getManufacturerCode(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
     method public String getMmsUAProfUrl();
@@ -45173,8 +45172,8 @@
     method public int getSimState();
     method public int getSimState(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSubscriberId();
-    method public String getTypeAllocationCode();
-    method public String getTypeAllocationCode(int);
+    method @Nullable public String getTypeAllocationCode();
+    method @Nullable public String getTypeAllocationCode(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @NonNull public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVisualVoicemailPackageName();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
@@ -45470,6 +45469,7 @@
     field public static final int TYPE_HIPRI = 16; // 0x10
     field public static final int TYPE_IA = 256; // 0x100
     field public static final int TYPE_IMS = 64; // 0x40
+    field public static final int TYPE_MCX = 1024; // 0x400
     field public static final int TYPE_MMS = 2; // 0x2
     field public static final int TYPE_SUPL = 4; // 0x4
   }
@@ -45559,7 +45559,7 @@
   }
 
   public class EuiccManager {
-    method public android.telephony.euicc.EuiccManager createForCardId(int);
+    method @NonNull public android.telephony.euicc.EuiccManager createForCardId(int);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void deleteSubscription(int, android.app.PendingIntent);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
     method @Nullable public String getEid();
@@ -47112,7 +47112,7 @@
 
   public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
     ctor public LineHeightSpan.Standard(@Px @IntRange(from=1) int);
-    ctor public LineHeightSpan.Standard(android.os.Parcel);
+    ctor public LineHeightSpan.Standard(@NonNull android.os.Parcel);
     method public void chooseHeight(@NonNull CharSequence, int, int, int, int, @NonNull android.graphics.Paint.FontMetricsInt);
     method public int describeContents();
     method @Px public int getHeight();
@@ -53036,7 +53036,7 @@
 
   public final class ContentCaptureContext implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public static android.view.contentcapture.ContentCaptureContext forLocusId(@NonNull android.net.Uri);
+    method @NonNull public static android.view.contentcapture.ContentCaptureContext forLocusId(@NonNull String);
     method @Nullable public android.os.Bundle getExtras();
     method @NonNull public android.content.LocusId getLocusId();
     method public void writeToParcel(android.os.Parcel, int);
@@ -53084,18 +53084,19 @@
     method public boolean isForEverything();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.UserDataRemovalRequest> CREATOR;
+    field public static final int FLAG_IS_PREFIX = 1; // 0x1
   }
 
   public static final class UserDataRemovalRequest.Builder {
     ctor public UserDataRemovalRequest.Builder();
-    method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder addLocusId(@NonNull android.content.LocusId, boolean);
+    method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder addLocusId(@NonNull android.content.LocusId, int);
     method @NonNull public android.view.contentcapture.UserDataRemovalRequest build();
     method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder forEverything();
   }
 
   public final class UserDataRemovalRequest.LocusIdRequest {
+    method @NonNull public int getFlags();
     method @NonNull public android.content.LocusId getLocusId();
-    method @NonNull public boolean isRecursive();
   }
 
 }
@@ -57331,7 +57332,7 @@
     method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
     method @Nullable public android.graphics.drawable.Drawable getTextCursorDrawable();
-    method public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
+    method @NonNull public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
     method @NonNull public java.util.Locale getTextLocale();
     method @NonNull @Size(min=1) public android.os.LocaleList getTextLocales();
     method @NonNull public android.text.PrecomputedText.Params getTextMetricsParams();
diff --git a/api/system-current.txt b/api/system-current.txt
index 8b2109f..8dedd2c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -181,6 +181,7 @@
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
+    field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
@@ -362,27 +363,34 @@
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
-    field public static final int UID_STATE_BACKGROUND = 5; // 0x5
-    field public static final int UID_STATE_CACHED = 6; // 0x6
-    field public static final int UID_STATE_FOREGROUND = 4; // 0x4
-    field public static final int UID_STATE_FOREGROUND_SERVICE = 3; // 0x3
-    field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 2; // 0x2
-    field public static final int UID_STATE_PERSISTENT = 0; // 0x0
-    field public static final int UID_STATE_TOP = 1; // 0x1
+    field public static final int OP_FLAGS_ALL = 31; // 0x1f
+    field public static final int OP_FLAGS_ALL_TRUSTED = 13; // 0xd
+    field public static final int OP_FLAG_SELF = 1; // 0x1
+    field public static final int OP_FLAG_TRUSTED_PROXIED = 8; // 0x8
+    field public static final int OP_FLAG_TRUSTED_PROXY = 2; // 0x2
+    field public static final int OP_FLAG_UNTRUSTED_PROXIED = 16; // 0x10
+    field public static final int OP_FLAG_UNTRUSTED_PROXY = 4; // 0x4
+    field public static final int UID_STATE_BACKGROUND = 600; // 0x258
+    field public static final int UID_STATE_CACHED = 700; // 0x2bc
+    field public static final int UID_STATE_FOREGROUND = 500; // 0x1f4
+    field public static final int UID_STATE_FOREGROUND_SERVICE = 400; // 0x190
+    field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; // 0x12c
+    field public static final int UID_STATE_PERSISTENT = 100; // 0x64
+    field public static final int UID_STATE_TOP = 200; // 0xc8
   }
 
   public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
     method public int describeContents();
-    method public long getAccessCount(int);
-    method public long getAccessDuration(int);
-    method public long getBackgroundAccessCount();
-    method public long getBackgroundAccessDuration();
-    method public long getBackgroundRejectCount();
-    method public long getForegroundAccessCount();
-    method public long getForegroundAccessDuration();
-    method public long getForegroundRejectCount();
+    method public long getAccessCount(int, int, int);
+    method public long getAccessDuration(int, int, int);
+    method public long getBackgroundAccessCount(int);
+    method public long getBackgroundAccessDuration(int);
+    method public long getBackgroundRejectCount(int);
+    method public long getForegroundAccessCount(int);
+    method public long getForegroundAccessDuration(int);
+    method public long getForegroundRejectCount(int);
     method @NonNull public String getOpName();
-    method public long getRejectCount(int);
+    method public long getRejectCount(int, int, int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR;
   }
@@ -404,6 +412,7 @@
   public static final class AppOpsManager.HistoricalOpsRequest.Builder {
     ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int);
@@ -431,17 +440,24 @@
 
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
-    method public int getDuration();
-    method public long getLastAccessBackgroundTime();
-    method public long getLastAccessForegroundTime();
-    method public long getLastAccessTime();
-    method public long getLastRejectBackgroundTime();
-    method public long getLastRejectForegroundTime();
-    method public long getLastRejectTime();
+    method public long getDuration();
+    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 public long getLastDuration(int, int, int);
+    method public long getLastForegroundDuration(int);
+    method public long getLastRejectBackgroundTime(int);
+    method public long getLastRejectForegroundTime(int);
+    method public long getLastRejectTime(int);
+    method public long getLastRejectTime(int, int, int);
     method public int getMode();
-    method public String getOpStr();
-    method public String getProxyPackageName();
+    method @NonNull public String getOpStr();
+    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(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
@@ -450,7 +466,7 @@
   public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
     method public int describeContents();
     method public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
-    method public String getPackageName();
+    method @NonNull public String getPackageName();
     method public int getUid();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
@@ -936,7 +952,7 @@
 
   public final class ClassificationsRequest implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public java.util.List<android.app.contentsuggestions.ContentSelection> getSelections();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.contentsuggestions.ClassificationsRequest> CREATOR;
@@ -944,8 +960,8 @@
 
   public static final class ClassificationsRequest.Builder {
     ctor public ClassificationsRequest.Builder(@NonNull java.util.List<android.app.contentsuggestions.ContentSelection>);
-    method public android.app.contentsuggestions.ClassificationsRequest build();
-    method public android.app.contentsuggestions.ClassificationsRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.app.contentsuggestions.ClassificationsRequest build();
+    method @NonNull public android.app.contentsuggestions.ClassificationsRequest.Builder setExtras(@NonNull android.os.Bundle);
   }
 
   public final class ContentClassification implements android.os.Parcelable {
@@ -983,7 +999,7 @@
 
   public final class SelectionsRequest implements android.os.Parcelable {
     method public int describeContents();
-    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public android.os.Bundle getExtras();
     method @Nullable public android.graphics.Point getInterestPoint();
     method public int getTaskId();
     method public void writeToParcel(android.os.Parcel, int);
@@ -992,9 +1008,9 @@
 
   public static final class SelectionsRequest.Builder {
     ctor public SelectionsRequest.Builder(int);
-    method public android.app.contentsuggestions.SelectionsRequest build();
-    method public android.app.contentsuggestions.SelectionsRequest.Builder setExtras(@NonNull android.os.Bundle);
-    method public android.app.contentsuggestions.SelectionsRequest.Builder setInterestPoint(@NonNull android.graphics.Point);
+    method @NonNull public android.app.contentsuggestions.SelectionsRequest build();
+    method @NonNull public android.app.contentsuggestions.SelectionsRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.app.contentsuggestions.SelectionsRequest.Builder setInterestPoint(@NonNull android.graphics.Point);
   }
 
 }
@@ -1172,7 +1188,7 @@
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
     method public int getUsageSource();
-    method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @Nullable android.app.PendingIntent);
+    method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
     method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -3525,7 +3541,7 @@
     method public void stop();
   }
 
-  public static class HwAudioSource.Builder {
+  public static final class HwAudioSource.Builder {
     ctor public HwAudioSource.Builder();
     method @NonNull public android.media.HwAudioSource build();
     method @NonNull public android.media.HwAudioSource.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
@@ -3538,7 +3554,7 @@
     field public static final int RADIO_TUNER = 1998; // 0x7ce
   }
 
-  public static class MediaTimestamp.Builder {
+  public static final class MediaTimestamp.Builder {
     ctor public MediaTimestamp.Builder();
     ctor public MediaTimestamp.Builder(@NonNull android.media.MediaTimestamp);
     method @NonNull public android.media.MediaTimestamp build();
@@ -3554,14 +3570,14 @@
     method public void stop();
   }
 
-  public static class SubtitleData.Builder {
+  public static final class SubtitleData.Builder {
     ctor public SubtitleData.Builder();
     ctor public SubtitleData.Builder(@NonNull android.media.SubtitleData);
     method @NonNull public android.media.SubtitleData build();
     method @NonNull public android.media.SubtitleData.Builder setSubtitleData(int, long, long, @NonNull byte[]);
   }
 
-  public static class TimedMetaData.Builder {
+  public static final class TimedMetaData.Builder {
     ctor public TimedMetaData.Builder();
     ctor public TimedMetaData.Builder(@NonNull android.media.TimedMetaData);
     method @NonNull public android.media.TimedMetaData build();
@@ -3663,7 +3679,7 @@
     method @Nullable public android.media.audiopolicy.AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull android.media.AudioAttributes);
     method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes);
     method public int getVolumeGroupIdForLegacyStreamType(int);
-    method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
+    method @NonNull public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator();
     method public int size();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategies> CREATOR;
@@ -3680,7 +3696,7 @@
 
   public final class AudioVolumeGroup implements android.os.Parcelable {
     method public int describeContents();
-    method public java.util.List<android.media.AudioAttributes> getAudioAttributes();
+    method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributes();
     method public int getId();
     method @NonNull public int[] getLegacyStreamTypes();
     method @NonNull public String name();
@@ -3692,7 +3708,7 @@
     ctor public AudioVolumeGroups();
     method public int describeContents();
     method @Nullable public android.media.audiopolicy.AudioVolumeGroup getById(int);
-    method public java.util.Iterator<android.media.audiopolicy.AudioVolumeGroup> iterator();
+    method @NonNull public java.util.Iterator<android.media.audiopolicy.AudioVolumeGroup> iterator();
     method public int size();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioVolumeGroups> CREATOR;
@@ -5673,6 +5689,7 @@
     method @NonNull public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onGetPermissionUsages(boolean, long);
     method public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream);
     method public abstract boolean onIsApplicationQualifiedForRole(@NonNull String, @NonNull String);
+    method public abstract boolean onIsRoleVisible(@NonNull String);
     method @BinderThread public abstract boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle);
     method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream);
     method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String);
@@ -5692,7 +5709,7 @@
   }
 
   public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
-    ctor public RuntimePermissionPresentationInfo(CharSequence, boolean, boolean);
+    ctor public RuntimePermissionPresentationInfo(@NonNull CharSequence, boolean, boolean);
     method public int describeContents();
     method @NonNull public CharSequence getLabel();
     method public boolean isGranted();
@@ -5836,19 +5853,17 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(String, String, String, boolean);
     field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
+    field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
     field public static final String NAMESPACE_AUTOFILL = "autofill";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
     field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
     field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
+    field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
     field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+    field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
   }
 
-  public static interface DeviceConfig.ActivityManagerNativeBoot {
-    field public static final String NAMESPACE = "activity_manager_native_boot";
-    field public static final String OFFLOAD_QUEUE_ENABLED = "offload_queue_enabled";
-  }
-
   public static interface DeviceConfig.AttentionManagerService {
     field public static final String COMPONENT_NAME = "component_name";
     field public static final String NAMESPACE = "attention_manager_service";
@@ -5867,10 +5882,6 @@
     field public static final String NAMESPACE = "intelligence_attention";
   }
 
-  public static interface DeviceConfig.MediaNative {
-    field public static final String NAMESPACE = "media_native";
-  }
-
   public static interface DeviceConfig.OnPropertyChangedListener {
     method public void onPropertyChanged(String, String, String);
   }
@@ -5897,10 +5908,6 @@
     field public static final String NAMESPACE = "runtime_native";
   }
 
-  public static interface DeviceConfig.RuntimeNativeBoot {
-    field public static final String NAMESPACE = "runtime_native_boot";
-  }
-
   public static interface DeviceConfig.Scheduler {
     field public static final String ENABLE_FAST_METRICS_COLLECTION = "enable_fast_metrics_collection";
     field public static final String NAMESPACE = "scheduler";
@@ -6418,8 +6425,8 @@
 
   public abstract class ApnService extends android.app.Service {
     ctor public ApnService();
-    method public android.os.IBinder onBind(android.content.Intent);
-    method @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
+    method @NonNull public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @WorkerThread @NonNull public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
   }
 
 }
@@ -6756,15 +6763,15 @@
     method public static android.view.textclassifier.TextClassifier getDefaultTextClassifierImplementation(@NonNull android.content.Context);
     method @Deprecated public final android.view.textclassifier.TextClassifier getLocalTextClassifier();
     method @Nullable public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void onClassifyText(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassification.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
-    method public void onCreateTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext, @NonNull android.view.textclassifier.TextClassificationSessionId);
-    method public void onDestroyTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationSessionId);
-    method public void onDetectLanguage(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLanguage.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
-    method public abstract void onGenerateLinks(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLinks.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
-    method @Deprecated public void onSelectionEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.SelectionEvent);
-    method public void onSuggestConversationActions(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.ConversationActions.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
-    method public abstract void onSuggestSelection(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextSelection.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
-    method public void onTextClassifierEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassifierEvent);
+    method @MainThread public abstract void onClassifyText(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassification.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
+    method @MainThread public void onCreateTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext, @NonNull android.view.textclassifier.TextClassificationSessionId);
+    method @MainThread public void onDestroyTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationSessionId);
+    method @MainThread public void onDetectLanguage(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLanguage.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>);
+    method @MainThread public abstract void onGenerateLinks(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLinks.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
+    method @Deprecated @MainThread public void onSelectionEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.SelectionEvent);
+    method @MainThread public void onSuggestConversationActions(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.ConversationActions.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>);
+    method @MainThread public abstract void onSuggestSelection(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextSelection.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
+    method @MainThread public void onTextClassifierEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassifierEvent);
     field public static final String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
   }
 
@@ -7070,8 +7077,8 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
-    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(android.telecom.PhoneAccountHandle);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(@Nullable String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
     field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
     field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
@@ -7091,12 +7098,12 @@
     field public static final int WWAN = 1; // 0x1
   }
 
-  public class CallAttributes implements android.os.Parcelable {
-    ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality);
+  public final class CallAttributes implements android.os.Parcelable {
+    ctor public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality);
     method public int describeContents();
-    method public android.telephony.CallQuality getCallQuality();
+    method @NonNull public android.telephony.CallQuality getCallQuality();
     method public int getNetworkType();
-    method public android.telephony.PreciseCallState getPreciseCallState();
+    method @NonNull public android.telephony.PreciseCallState getPreciseCallState();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
   }
@@ -7133,13 +7140,13 @@
   }
 
   public final class CarrierRestrictionRules implements android.os.Parcelable {
+    method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method public int describeContents();
     method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers();
     method public int getDefaultCarrierRestriction();
     method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
     method public int getMultiSimPolicy();
     method public boolean isAllCarriersAllowed();
-    method public java.util.List<java.lang.Boolean> isCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1
     field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0
@@ -7148,13 +7155,13 @@
     field public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1; // 0x1
   }
 
-  public static class CarrierRestrictionRules.Builder {
-    method public android.telephony.CarrierRestrictionRules build();
-    method public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed();
-    method public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(java.util.List<android.service.carrier.CarrierIdentifier>);
-    method public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int);
-    method public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(java.util.List<android.service.carrier.CarrierIdentifier>);
-    method public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
+  public static final class CarrierRestrictionRules.Builder {
+    method @NonNull public android.telephony.CarrierRestrictionRules build();
+    method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed();
+    method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
+    method @NonNull public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int);
+    method @NonNull public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
+    method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
   }
 
   public final class DataFailCause {
@@ -7944,7 +7951,7 @@
     method @Deprecated public boolean getDataEnabled(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getLogicalToPhysicalSlotMapping();
     method public static long getMaxNumberVerificationTimeoutMillis();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmap();
@@ -8420,7 +8427,7 @@
   }
 
   public class ImsCallSessionListener {
-    method public void callQualityChanged(android.telephony.CallQuality);
+    method public void callQualityChanged(@NonNull android.telephony.CallQuality);
     method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
     method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -8445,7 +8452,7 @@
     method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
-    method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
+    method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
     method public void callSessionRttMessageReceived(String);
     method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionRttModifyResponseReceived(int);
@@ -8749,12 +8756,12 @@
   public final class ImsSsData implements android.os.Parcelable {
     ctor public ImsSsData(int, int, int, int, int);
     method public int describeContents();
-    method public android.telephony.ims.ImsCallForwardInfo[] getCallForwardInfo();
+    method @Nullable public java.util.List<android.telephony.ims.ImsCallForwardInfo> getCallForwardInfo();
     method public int getRequestType();
     method public int getResult();
     method public int getServiceClass();
     method public int getServiceType();
-    method @NonNull public android.telephony.ims.ImsSsInfo[] getSuppServiceInfo();
+    method @NonNull public java.util.List<android.telephony.ims.ImsSsInfo> getSuppServiceInfo();
     method public int getTeleserviceType();
     method public boolean isTypeBarring();
     method public boolean isTypeCf();
@@ -8814,11 +8821,11 @@
     field public static final int SS_WAIT = 12; // 0xc
   }
 
-  public static class ImsSsData.Builder {
+  public static final class ImsSsData.Builder {
     ctor public ImsSsData.Builder(int, int, int, int, int);
     method @NonNull public android.telephony.ims.ImsSsData build();
-    method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull android.telephony.ims.ImsCallForwardInfo[]);
-    method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull android.telephony.ims.ImsSsInfo[]);
+    method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull java.util.List<android.telephony.ims.ImsCallForwardInfo>);
+    method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull java.util.List<android.telephony.ims.ImsSsInfo>);
   }
 
   public final class ImsSsInfo implements android.os.Parcelable {
@@ -8827,7 +8834,7 @@
     method public int getClirInterrogationStatus();
     method public int getClirOutgoingState();
     method @Deprecated public String getIcbNum();
-    method public String getIncomingCommunicationBarringNumber();
+    method @Nullable public String getIncomingCommunicationBarringNumber();
     method public int getProvisionStatus();
     method public int getStatus();
     method public void writeToParcel(android.os.Parcel, int);
@@ -8848,7 +8855,7 @@
     field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff
   }
 
-  public static class ImsSsInfo.Builder {
+  public static final class ImsSsInfo.Builder {
     ctor public ImsSsInfo.Builder(int);
     method @NonNull public android.telephony.ims.ImsSsInfo build();
     method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int);
@@ -9348,7 +9355,7 @@
     method public static void logFileOperationFailure(int, int);
     method public static void logFilePick(int, long, int, boolean, int, int, int);
     method public static void logInvalidScopedAccessRequest(int);
-    method public static void logPickerLaunchedFrom(String);
+    method public static void logPickerLaunchedFrom(@Nullable String);
     method public static void logRootVisited(int, int);
     method public static void logSearchMode(int);
     method public static void logSearchType(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 4f7605b..684524e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -193,29 +193,36 @@
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_COARSE_LOCATION = 0; // 0x0
+    field public static final int OP_FLAGS_ALL = 31; // 0x1f
+    field public static final int OP_FLAG_SELF = 1; // 0x1
+    field public static final int OP_FLAG_TRUSTED_PROXIED = 8; // 0x8
+    field public static final int OP_FLAG_TRUSTED_PROXY = 2; // 0x2
+    field public static final int OP_FLAG_UNTRUSTED_PROXIED = 16; // 0x10
+    field public static final int OP_FLAG_UNTRUSTED_PROXY = 4; // 0x4
     field public static final int OP_RECORD_AUDIO = 27; // 0x1b
+    field public static final int OP_START_FOREGROUND = 76; // 0x4c
     field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
-    field public static final int UID_STATE_BACKGROUND = 5; // 0x5
-    field public static final int UID_STATE_CACHED = 6; // 0x6
-    field public static final int UID_STATE_FOREGROUND = 4; // 0x4
-    field public static final int UID_STATE_FOREGROUND_SERVICE = 3; // 0x3
-    field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 2; // 0x2
-    field public static final int UID_STATE_PERSISTENT = 0; // 0x0
-    field public static final int UID_STATE_TOP = 1; // 0x1
+    field public static final int UID_STATE_BACKGROUND = 600; // 0x258
+    field public static final int UID_STATE_CACHED = 700; // 0x2bc
+    field public static final int UID_STATE_FOREGROUND = 500; // 0x1f4
+    field public static final int UID_STATE_FOREGROUND_SERVICE = 400; // 0x190
+    field public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; // 0x12c
+    field public static final int UID_STATE_PERSISTENT = 100; // 0x64
+    field public static final int UID_STATE_TOP = 200; // 0xc8
   }
 
   public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
     method public int describeContents();
-    method public long getAccessCount(int);
-    method public long getAccessDuration(int);
-    method public long getBackgroundAccessCount();
-    method public long getBackgroundAccessDuration();
-    method public long getBackgroundRejectCount();
-    method public long getForegroundAccessCount();
-    method public long getForegroundAccessDuration();
-    method public long getForegroundRejectCount();
+    method public long getAccessCount(int, int, int);
+    method public long getAccessDuration(int, int, int);
+    method public long getBackgroundAccessCount(int);
+    method public long getBackgroundAccessDuration(int);
+    method public long getBackgroundRejectCount(int);
+    method public long getForegroundAccessCount(int);
+    method public long getForegroundAccessDuration(int);
+    method public long getForegroundRejectCount(int);
     method @NonNull public String getOpName();
-    method public long getRejectCount(int);
+    method public long getRejectCount(int, int, int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR;
   }
@@ -228,9 +235,9 @@
     method public int getUidCount();
     method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int);
     method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(int);
-    method public void increaseAccessCount(int, int, @NonNull String, int, long);
-    method public void increaseAccessDuration(int, int, @NonNull String, int, long);
-    method public void increaseRejectCount(int, int, @NonNull String, int, long);
+    method public void increaseAccessCount(int, int, @NonNull String, int, int, long);
+    method public void increaseAccessDuration(int, int, @NonNull String, int, int, long);
+    method public void increaseRejectCount(int, int, @NonNull String, int, int, long);
     method public void offsetBeginAndEndTime(long);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
@@ -242,6 +249,7 @@
   public static final class AppOpsManager.HistoricalOpsRequest.Builder {
     ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int);
@@ -271,6 +279,31 @@
     method public void onOpActiveChanged(int, int, String, boolean);
   }
 
+  public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getDuration();
+    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 public long getLastDuration(int, int, int);
+    method public long getLastForegroundDuration(int);
+    method public long getLastRejectBackgroundTime(int);
+    method public long getLastRejectForegroundTime(int);
+    method public long getLastRejectTime(int);
+    method public long getLastRejectTime(int, int, int);
+    method public int getMode();
+    method @NonNull public String getOpStr();
+    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(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
+  }
+
   public class DownloadManager {
     field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
   }
@@ -978,7 +1011,7 @@
     method public android.media.MediaPlayer2.DrmInfo getDrmInfo(@NonNull android.media.DataSourceDesc);
     method public android.media.MediaDrm.KeyRequest getDrmKeyRequest(@NonNull android.media.DataSourceDesc, @Nullable byte[], @Nullable byte[], @Nullable String, int, @Nullable java.util.Map<java.lang.String,java.lang.String>) throws android.media.MediaPlayer2.NoDrmSchemeException;
     method public String getDrmPropertyString(@NonNull android.media.DataSourceDesc, @NonNull String) throws android.media.MediaPlayer2.NoDrmSchemeException;
-    method public Object prepareDrm(@NonNull android.media.DataSourceDesc, @NonNull java.util.UUID);
+    method @NonNull public Object prepareDrm(@NonNull android.media.DataSourceDesc, @NonNull java.util.UUID);
     method public byte[] provideDrmKeyResponse(@NonNull android.media.DataSourceDesc, @Nullable byte[], @NonNull byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException;
     method public void releaseDrm(@NonNull android.media.DataSourceDesc) throws android.media.MediaPlayer2.NoDrmSchemeException;
     method public void restoreDrmKeys(@NonNull android.media.DataSourceDesc, @NonNull byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException;
@@ -3000,7 +3033,7 @@
     method public abstract String value();
   }
 
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface InspectableProperty {
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD}) public @interface InspectableProperty {
     method public abstract int attributeId() default android.content.res.Resources.ID_NULL;
     method public abstract android.view.inspector.InspectableProperty.EnumMap[] enumMapping() default {};
     method public abstract android.view.inspector.InspectableProperty.FlagMap[] flagMapping() default {};
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 012310c..77a56e5 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -142,7 +142,7 @@
     }
     if (lineSize - lastIndex > 0) {
         int beginning = lastIndex;
-        if (record.size() == indices.size()) {
+        if (record.size() == indices.size() && !record.empty()) {
             // We've already encountered all of the columns...put whatever is
             // left in the last column.
             record.pop_back();
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
index 21ced9c..5d525e6 100644
--- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -65,8 +65,9 @@
         if (line.empty()) continue;
 
         nline++;
-
-        if (stripPrefix(&line, "Tasks:")) {
+        // The format changes from time to time in toybox/toys/posix/ps.c
+        // With -H, it prints Threads instead of Tasks (FLAG(H)?"Thread":"Task")
+        if (stripPrefix(&line, "Threads:")) {
             writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER,
                 CpuInfoProto::TaskStats::_FIELD_COUNT,
                 CpuInfoProto::TaskStats::_FIELD_NAMES,
diff --git a/cmds/incident_helper/testdata/cpuinfo.txt b/cmds/incident_helper/testdata/cpuinfo.txt
index ec4a839..aa3afc3 100644
--- a/cmds/incident_helper/testdata/cpuinfo.txt
+++ b/cmds/incident_helper/testdata/cpuinfo.txt
@@ -1,8 +1,8 @@
-Tasks: 2038 total,   1 running,2033 sleeping,   0 stopped,   0 zombie
+Threads: 2038 total,   1 running,2033 sleeping,   0 stopped,   0 zombie
 
-Mem:   3842668k total,  3761936k used,    80732k free,   220188k buffers
+  Mem:   3842668k total,  3761936k used,    80732k free,   220188k buffers
 
-Swap:   524284k total,    25892k used,   498392k free,  1316952k cached
+ Swap:   524284k total,    25892k used,   498392k free,  1316952k cached
 
 400%cpu  17%user   0%nice  43%sys 338%idle   0%iow   0%irq   1%sirq   0%host
 
@@ -12,4 +12,4 @@
 29438 29438 rootabcdefghij 20 0 57.9 R  14M 3.8M     top test        top
   916   916 system       18  -2  1.4 S 4.6G 404M  fg system_server   system_server
    28    28 root         -2   0  1.4 S    0    0  bg rcuc/3          [rcuc/3]
-   27    27 root         RT   0  1.4 S    0    0  ta migration/3     [migration/3]
\ No newline at end of file
+   27    27 root         RT   0  1.4 S    0    0  ta migration/3     [migration/3]
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index da72002..7298da6 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -219,6 +219,7 @@
         "tests/anomaly/AnomalyTracker_test.cpp",
         "tests/ConfigManager_test.cpp",
         "tests/external/puller_util_test.cpp",
+        "tests/external/IncidentReportArgs_test.cpp",
         "tests/external/StatsPuller_test.cpp",
         "tests/indexed_priority_queue_test.cpp",
         "tests/LogEntryMatcher_test.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index a983b27..5968fa8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1568,7 +1568,7 @@
         ROLLBACK_INITIATE = 1;
         ROLLBACK_SUCCESS = 2;
         ROLLBACK_FAILURE = 3;
-        ROLLBACK_ROOT_TRIGGERED = 4;
+        ROLLBACK_BOOT_TRIGGERED = 4;
     }
     optional RollbackType rollback_type = 1;
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 5c6d548..0e91f52 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -324,6 +324,12 @@
     EXPLICIT = 1;
   }
   optional Destination dest = 2;
+
+  // Package name of the incident report receiver.
+  optional string receiver_pkg = 3;
+
+  // Class name of the incident report receiver.
+  optional string receiver_cls = 4;
 }
 
 message PerfettoDetails {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index 0ed2d75..7c2d242 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -162,6 +162,10 @@
     }
     incidentReport.setDest(dest);
 
+    incidentReport.setReceiverPkg(config.receiver_pkg());
+
+    incidentReport.setReceiverCls(config.receiver_cls());
+
     sp<IIncidentManager> service = interface_cast<IIncidentManager>(
             defaultServiceManager()->getService(android::String16("incident")));
     if (service == nullptr) {
diff --git a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp b/cmds/statsd/tests/external/IncidentReportArgs_test.cpp
new file mode 100644
index 0000000..c170b12
--- /dev/null
+++ b/cmds/statsd/tests/external/IncidentReportArgs_test.cpp
@@ -0,0 +1,72 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android/os/IncidentReportArgs.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(IncidentReportArgsTest, testSerialization) {
+    IncidentReportArgs args;
+    args.setAll(0);
+    args.addSection(1000);
+    args.addSection(1001);
+
+    vector<uint8_t> header1;
+    header1.push_back(0x1);
+    header1.push_back(0x2);
+    vector<uint8_t> header2;
+    header1.push_back(0x22);
+    header1.push_back(0x33);
+
+    args.addHeader(header1);
+    args.addHeader(header2);
+
+    args.setDest(1);
+
+    args.setReceiverPkg("com.android.os");
+    args.setReceiverCls("com.android.os.Receiver");
+
+    Parcel out;
+    status_t err = args.writeToParcel(&out);
+    EXPECT_EQ(NO_ERROR, err);
+
+    out.setDataPosition(0);
+
+    IncidentReportArgs args2;
+    err = args2.readFromParcel(&out);
+    EXPECT_EQ(NO_ERROR, err);
+
+    EXPECT_EQ(0, args2.all());
+    set<int> sections;
+    sections.insert(1000);
+    sections.insert(1001);
+    EXPECT_EQ(sections, args2.sections());
+    EXPECT_EQ(1, args2.dest());
+
+    EXPECT_EQ(String16("com.android.os"), args2.receiverPkg());
+    EXPECT_EQ(String16("com.android.os.Receiver"), args2.receiverCls());
+
+    vector<vector<uint8_t>> headers;
+    headers.push_back(header1);
+    headers.push_back(header2);
+    EXPECT_EQ(headers, args2.headers());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 46a956c..9b0e657 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2935,7 +2935,6 @@
 Lcom/android/internal/telephony/ServiceStateTracker;->isInvalidOperatorNumeric(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ServiceStateTracker;->log(Ljava/lang/String;)V
 Lcom/android/internal/telephony/ServiceStateTracker;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/ServiceStateTracker;->mAttachedRegistrants:Landroid/os/RegistrantList;
 Lcom/android/internal/telephony/ServiceStateTracker;->mCi:Lcom/android/internal/telephony/CommandsInterface;
 Lcom/android/internal/telephony/ServiceStateTracker;->mCr:Landroid/content/ContentResolver;
 Lcom/android/internal/telephony/ServiceStateTracker;->mCurDataSpn:Ljava/lang/String;
@@ -2947,7 +2946,6 @@
 Lcom/android/internal/telephony/ServiceStateTracker;->mDataRoamingOnRegistrants:Landroid/os/RegistrantList;
 Lcom/android/internal/telephony/ServiceStateTracker;->mDefaultRoamingIndicator:I
 Lcom/android/internal/telephony/ServiceStateTracker;->mDesiredPowerState:Z
-Lcom/android/internal/telephony/ServiceStateTracker;->mDetachedRegistrants:Landroid/os/RegistrantList;
 Lcom/android/internal/telephony/ServiceStateTracker;->mDeviceShuttingDown:Z
 Lcom/android/internal/telephony/ServiceStateTracker;->mEmergencyOnly:Z
 Lcom/android/internal/telephony/ServiceStateTracker;->mIccRecords:Lcom/android/internal/telephony/uicc/IccRecords;
@@ -2975,7 +2973,6 @@
 Lcom/android/internal/telephony/ServiceStateTracker;->mUiccController:Lcom/android/internal/telephony/uicc/UiccController;
 Lcom/android/internal/telephony/ServiceStateTracker;->mVoiceRoamingOffRegistrants:Landroid/os/RegistrantList;
 Lcom/android/internal/telephony/ServiceStateTracker;->mVoiceRoamingOnRegistrants:Landroid/os/RegistrantList;
-Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V
 Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z
 Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V
 Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b654258..08239a1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -332,7 +332,6 @@
     String[] mInstrumentedSplitAppDirs = null;
     String mInstrumentedLibDir = null;
     boolean mSystemThread = false;
-    boolean mJitEnabled = false;
     boolean mSomeActivitiesChanged = false;
     boolean mUpdatingSystemConfig = false;
     /* package */ boolean mHiddenApiWarningShown = false;
@@ -1696,7 +1695,6 @@
         public static final int SUICIDE                 = 130;
         @UnsupportedAppUsage
         public static final int REMOVE_PROVIDER         = 131;
-        public static final int ENABLE_JIT              = 132;
         public static final int DISPATCH_PACKAGE_BROADCAST = 133;
         @UnsupportedAppUsage
         public static final int SCHEDULE_CRASH          = 134;
@@ -1746,7 +1744,6 @@
                     case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
                     case SUICIDE: return "SUICIDE";
                     case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
-                    case ENABLE_JIT: return "ENABLE_JIT";
                     case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
                     case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
                     case DUMP_HEAP: return "DUMP_HEAP";
@@ -1858,9 +1855,6 @@
                     completeRemoveProvider((ProviderRefCount)msg.obj);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
-                case ENABLE_JIT:
-                    ensureJitEnabled();
-                    break;
                 case DISPATCH_PACKAGE_BROADCAST:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastPackage");
                     handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj);
@@ -1996,7 +1990,6 @@
             if (stopProfiling) {
                 mProfiler.stopProfiling();
             }
-            ensureJitEnabled();
             return false;
         }
     }
@@ -2330,13 +2323,6 @@
         }
     }
 
-    void ensureJitEnabled() {
-        if (!mJitEnabled) {
-            mJitEnabled = true;
-            dalvik.system.VMRuntime.getRuntime().startJitCompilation();
-        }
-    }
-
     @UnsupportedAppUsage
     void scheduleGcIdler() {
         if (!mGcIdlerScheduled) {
@@ -3782,7 +3768,6 @@
                         ActivityManager.getService().serviceDoneExecuting(
                                 data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                     }
-                    ensureJitEnabled();
                 } catch (RemoteException ex) {
                     throw ex.rethrowFromSystemServer();
                 }
@@ -3896,7 +3881,6 @@
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
-                ensureJitEnabled();
             } catch (Exception e) {
                 if (!mInstrumentation.onException(s, e)) {
                     throw new RuntimeException(
@@ -6177,9 +6161,6 @@
             if (!data.restrictedBackupMode) {
                 if (!ArrayUtils.isEmpty(data.providers)) {
                     installContentProviders(app, data.providers);
-                    // For process that contains content providers, we want to
-                    // ensure that the JIT is enabled "at some point".
-                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                 }
             }
 
@@ -6812,12 +6793,6 @@
         sCurrentActivityThread = this;
         mSystemThread = system;
         if (!system) {
-            ViewRootImpl.addFirstDrawHandler(new Runnable() {
-                @Override
-                public void run() {
-                    ensureJitEnabled();
-                }
-            });
             android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                     UserHandle.myUserId());
             RuntimeInit.setApplicationObject(mAppThread.asBinder());
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5bf7ef5..91df05f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -43,6 +43,8 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArrayMap;
+import android.util.LongSparseArray;
+import android.util.LongSparseLongArray;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -55,8 +57,10 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.ArrayList;
@@ -64,9 +68,11 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * API for interacting with "application operation" tracking.
@@ -227,7 +233,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = { "UID_STATE_" }, value = {
+    @IntDef(prefix = { "UID_STATE_" }, value = {
             UID_STATE_PERSISTENT,
             UID_STATE_TOP,
             UID_STATE_FOREGROUND_SERVICE_LOCATION,
@@ -239,78 +245,322 @@
     public @interface UidState {}
 
     /**
-     * Invalid UID state.
-     * @hide
-     */
-    public static final int UID_STATE_INVALID = -1;
-
-    /**
-     * Metrics about an op when its uid is persistent.
+     * Uid state: The UID is a foreground persistent app.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_PERSISTENT = 0;
+    public static final int UID_STATE_PERSISTENT = 100;
 
     /**
-     * Metrics about an op when its uid is at the top.
+     * Uid state: The UID is top foreground app.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_TOP = 1;
+    public static final int UID_STATE_TOP = 200;
 
     /**
-     * Metrics about an op when its uid is running a foreground service with location type.
+     * Uid state: The UID is running a foreground service of location type.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 2;
+    public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300;
 
     /**
-     * Metrics about an op when its uid is running a foreground service.
+     * Uid state: The UID is running a foreground service.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_FOREGROUND_SERVICE = 3;
+    public static final int UID_STATE_FOREGROUND_SERVICE = 400;
 
     /**
-     * Last UID state in which we don't restrict what an op can do.
+     * The max, which is min priority, UID state for which any app op
+     * would be considered as performed in the foreground.
      * @hide
      */
-    public static final int UID_STATE_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE_LOCATION;
+    public static final int UID_STATE_MAX_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE;
 
     /**
-     * Metrics about an op when its uid is in the foreground for any other reasons.
+     * Uid state: The UID is a foreground app.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_FOREGROUND = 4;
+    public static final int UID_STATE_FOREGROUND = 500;
 
     /**
-     * Metrics about an op when its uid is in the background for any reason.
+     * Uid state: The UID is a background app.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_BACKGROUND = 5;
+    public static final int UID_STATE_BACKGROUND = 600;
 
     /**
-     * Metrics about an op when its uid is cached.
+     * Uid state: The UID is a cached app.
      * @hide
      */
     @TestApi
     @SystemApi
-    public static final int UID_STATE_CACHED = 6;
+    public static final int UID_STATE_CACHED = 700;
 
     /**
-     * Number of uid states we track.
+     * Uid state: The UID state with the highest priority.
      * @hide
      */
-    public static final int _NUM_UID_STATE = 7;
+    public static final int MAX_PRIORITY_UID_STATE = UID_STATE_PERSISTENT;
+
+    /**
+     * Uid state: The UID state with the lowest priority.
+     * @hide
+     */
+    public static final int MIN_PRIORITY_UID_STATE = UID_STATE_CACHED;
+
+    /**
+     * Resolves the first unrestricted state given an app op. Location is
+     * special as we want to allow its access only if a dedicated location
+     * foreground service is running. For other ops we consider any foreground
+     * service as a foreground state.
+     *
+     * @param op The op to resolve.
+     * @return The last restricted UID state.
+     *
+     * @hide
+     */
+    public static int resolveFirstUnrestrictedUidState(int op) {
+        switch (op) {
+            case OP_FINE_LOCATION:
+            case OP_COARSE_LOCATION: {
+                return UID_STATE_FOREGROUND_SERVICE_LOCATION;
+            }
+        }
+        return UID_STATE_FOREGROUND_SERVICE;
+    }
+
+    /**
+     * Resolves the last restricted state given an app op. Location is
+     * special as we want to allow its access only if a dedicated location
+     * foreground service is running. For other ops we consider any foreground
+     * service as a foreground state.
+     *
+     * @param op The op to resolve.
+     * @return The last restricted UID state.
+     *
+     * @hide
+     */
+    public static int resolveLastRestrictedUidState(int op) {
+        switch (op) {
+            case OP_FINE_LOCATION:
+            case OP_COARSE_LOCATION: {
+                return UID_STATE_FOREGROUND_SERVICE;
+            }
+        }
+        return UID_STATE_FOREGROUND;
+    }
+
+    /** @hide Note: Keep these sorted */
+    public static final int[] UID_STATES = {
+            UID_STATE_PERSISTENT,
+            UID_STATE_TOP,
+            UID_STATE_FOREGROUND_SERVICE_LOCATION,
+            UID_STATE_FOREGROUND_SERVICE,
+            UID_STATE_FOREGROUND,
+            UID_STATE_BACKGROUND,
+            UID_STATE_CACHED
+    };
+
+    /** @hide */
+    public static String getUidStateName(@UidState int uidState) {
+        switch (uidState) {
+            case UID_STATE_PERSISTENT:
+                return "pers";
+            case UID_STATE_TOP:
+                return "top";
+            case UID_STATE_FOREGROUND_SERVICE_LOCATION:
+                return "fgsvcl";
+            case UID_STATE_FOREGROUND_SERVICE:
+                return "fgsvc";
+            case UID_STATE_FOREGROUND:
+                return "fg";
+            case UID_STATE_BACKGROUND:
+                return "bg";
+            case UID_STATE_CACHED:
+                return "cch";
+            default:
+                return "unknown";
+        }
+    }
+
+    /**
+     * Flag: non proxy operations. These are operations
+     * performed on behalf of the app itself and not on behalf of
+     * another one.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int OP_FLAG_SELF = 0x1;
+
+    /**
+     * Flag: trusted proxy operations. These are operations
+     * performed on behalf of another app by a trusted app.
+     * Which is work a trusted app blames on another app.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int OP_FLAG_TRUSTED_PROXY = 0x2;
+
+    /**
+     * Flag: untrusted proxy operations. These are operations
+     * performed on behalf of another app by an untrusted app.
+     * Which is work an untrusted app blames on another app.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int OP_FLAG_UNTRUSTED_PROXY = 0x4;
+
+    /**
+     * Flag: trusted proxied operations. These are operations
+     * performed by a trusted other app on behalf of an app.
+     * Which is work an app was blamed for by a trusted app.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int OP_FLAG_TRUSTED_PROXIED = 0x8;
+
+    /**
+     * Flag: untrusted proxied operations. These are operations
+     * performed by an untrusted other app on behalf of an app.
+     * Which is work an app was blamed for by an untrusted app.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int OP_FLAG_UNTRUSTED_PROXIED = 0x10;
+
+    /**
+     * Flags: all operations. These include operations matched
+     * by {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXIED},
+     * {@link #OP_FLAG_UNTRUSTED_PROXIED}, {@link #OP_FLAG_TRUSTED_PROXIED},
+     * {@link #OP_FLAG_UNTRUSTED_PROXIED}.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public static final int OP_FLAGS_ALL =
+            OP_FLAG_SELF
+                | OP_FLAG_TRUSTED_PROXY
+                | OP_FLAG_UNTRUSTED_PROXY
+                | OP_FLAG_TRUSTED_PROXIED
+                | OP_FLAG_UNTRUSTED_PROXIED;
+
+    /**
+     * Flags: all trusted operations which is ones either the app did {@link #OP_FLAG_SELF},
+     * or it was blamed for by a trusted app {@link #OP_FLAG_TRUSTED_PROXIED}, or ones the
+     * app if untrusted blamed on other apps {@link #OP_FLAG_UNTRUSTED_PROXY}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OP_FLAGS_ALL_TRUSTED = AppOpsManager.OP_FLAG_SELF
+        | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
+        | AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            OP_FLAG_SELF,
+            OP_FLAG_TRUSTED_PROXY,
+            OP_FLAG_UNTRUSTED_PROXY,
+            OP_FLAG_TRUSTED_PROXIED,
+            OP_FLAG_UNTRUSTED_PROXIED
+    })
+    public @interface OpFlags {}
+
+
+    /** @hide */
+    public static final String getFlagName(@OpFlags int flag) {
+        switch (flag) {
+            case OP_FLAG_SELF:
+                return "s";
+            case OP_FLAG_TRUSTED_PROXY:
+                return "tp";
+            case OP_FLAG_UNTRUSTED_PROXY:
+                return "up";
+            case OP_FLAG_TRUSTED_PROXIED:
+                return "tpd";
+            case OP_FLAG_UNTRUSTED_PROXIED:
+                return "upd";
+            default:
+                return "unknown";
+        }
+    }
+
+    private static final int UID_STATE_OFFSET = 31;
+    private static final int FLAGS_MASK = 0xFFFFFFFF;
+
+    /**
+     * Key for a data bucket storing app op state. The bucket
+     * is composed of the uid state and state flags. This way
+     * we can query data for given uid state and a set of flags where
+     * the flags control which type of data to get. For example,
+     * one can get the ops an app did on behalf of other apps
+     * while in the background.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+    public @interface DataBucketKey {
+    }
+
+    /** @hide */
+    public static String keyToString(@DataBucketKey long key) {
+        final int uidState = extractUidStateFromKey(key);
+        final int flags = extractFlagsFromKey(key);
+        return "[" + getUidStateName(uidState) + "-" + flagsToString(flags) + "]";
+    }
+
+    /** @hide */
+    public static @DataBucketKey long makeKey(@UidState int uidState, @OpFlags int flags) {
+        return ((long) uidState << UID_STATE_OFFSET) | flags;
+    }
+
+    /** @hide */
+    public static int extractUidStateFromKey(@DataBucketKey long key) {
+        return (int) (key >> UID_STATE_OFFSET);
+    }
+
+    /** @hide */
+    public static int extractFlagsFromKey(@DataBucketKey long key) {
+        return (int) (key & FLAGS_MASK);
+    }
+
+    /** @hide */
+    public static String flagsToString(@OpFlags int flags) {
+        final StringBuilder flagsBuilder = new StringBuilder();
+        while (flags != 0) {
+            final int flag = 1 << Integer.numberOfTrailingZeros(flags);
+            flags &= ~flag;
+            if (flagsBuilder.length() > 0) {
+                flagsBuilder.append('|');
+            }
+            flagsBuilder.append(getFlagName(flag));
+        }
+        return flagsBuilder.toString();
+    }
 
     // when adding one of these:
     //  - increment _NUM_OP
@@ -551,7 +801,7 @@
     @UnsupportedAppUsage
     public static final int OP_MANAGE_IPSEC_TUNNELS = 75;
     /** @hide Any app start foreground service. */
-    @UnsupportedAppUsage
+    @TestApi
     public static final int OP_START_FOREGROUND = 76;
     /** @hide */
     @UnsupportedAppUsage
@@ -1947,14 +2197,23 @@
             mEntries = entries;
         }
 
-        public String getPackageName() {
+        /**
+         * @return The name of the package.
+         */
+        public @NonNull String getPackageName() {
             return mPackageName;
         }
 
+        /**
+         * @return The uid of the package.
+         */
         public int getUid() {
             return mUid;
         }
 
+        /**
+         * @return The ops of the package.
+         */
         public List<OpEntry> getOps() {
             return mEntries;
         }
@@ -1999,57 +2258,59 @@
      * Class holding the information about one unique operation of an application.
      * @hide
      */
+    @TestApi
+    @Immutable
     @SystemApi
     public static final class OpEntry implements Parcelable {
         private final int mOp;
-        private final @Mode int mMode;
-        private final long[] mTimes;
-        private final long[] mRejectTimes;
-        private final int mDuration;
-        private final int mProxyUid;
         private final boolean mRunning;
-        private final String mProxyPackageName;
+        private final @Mode int mMode;
+        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;
 
         /**
          * @hide
          */
-        public OpEntry(int op, @Mode int mode, long time, long rejectTime, int duration,
-                int proxyUid, String proxyPackage) {
+        public OpEntry(int op, boolean running, @Mode int mode,
+                @Nullable LongSparseLongArray accessTimes, @Nullable LongSparseLongArray rejectTimes,
+                @Nullable LongSparseLongArray durations, @Nullable LongSparseLongArray proxyUids,
+                @Nullable LongSparseArray<String> proxyPackageNames) {
             mOp = op;
-            mMode = mode;
-            mTimes = new long[_NUM_UID_STATE];
-            mRejectTimes = new long[_NUM_UID_STATE];
-            mTimes[0] = time;
-            mRejectTimes[0] = rejectTime;
-            mDuration = duration;
-            mRunning = duration == -1;
-            mProxyUid = proxyUid;
-            mProxyPackageName = proxyPackage;
-        }
-
-        /**
-         * @hide
-         */
-        public OpEntry(int op, @Mode int mode, long[] times, long[] rejectTimes, int duration,
-                boolean running, int proxyUid, String proxyPackage) {
-            mOp = op;
-            mMode = mode;
-            mTimes = new long[_NUM_UID_STATE];
-            mRejectTimes = new long[_NUM_UID_STATE];
-            System.arraycopy(times, 0, mTimes, 0, _NUM_UID_STATE);
-            System.arraycopy(rejectTimes, 0, mRejectTimes, 0, _NUM_UID_STATE);
-            mDuration = duration;
             mRunning = running;
-            mProxyUid = proxyUid;
-            mProxyPackageName = proxyPackage;
+            mMode = mode;
+            mAccessTimes = accessTimes;
+            mRejectTimes = rejectTimes;
+            mDurations = durations;
+            mProxyUids = proxyUids;
+            mProxyPackageNames = proxyPackageNames;
         }
 
         /**
          * @hide
          */
-        public OpEntry(int op, @Mode int mode, long[] times, long[] rejectTimes, int duration,
-                int proxyUid, String proxyPackage) {
-            this(op, mode, times, rejectTimes, duration, duration == -1, proxyUid, proxyPackage);
+        public OpEntry(int op, @Mode int mode) {
+            mOp = op;
+            mMode = mode;
+            mRunning = false;
+            mAccessTimes = null;
+            mRejectTimes = null;
+            mDurations = null;
+            mProxyUids = null;
+            mProxyPackageNames = null;
+        }
+
+        /**
+         * 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;
         }
 
         /**
@@ -2061,14 +2322,14 @@
         }
 
         /**
-         * Return this entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
+         * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
          */
-        public String getOpStr() {
+        public @NonNull String getOpStr() {
             return sOpToString[mOp];
         }
 
         /**
-         * Return this entry's current mode, such as {@link #MODE_ALLOWED}.
+         * @return this entry's current mode, such as {@link #MODE_ALLOWED}.
          */
         public @Mode int getMode() {
             return mMode;
@@ -2079,89 +2340,331 @@
          */
         @UnsupportedAppUsage
         public long getTime() {
-            return maxTime(mTimes, 0, _NUM_UID_STATE);
+            return getLastAccessTime(OP_FLAGS_ALL);
         }
 
         /**
-         * Return the last wall clock time this op was accessed by the app.
+         * 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() {
-            return maxTime(mTimes, 0, _NUM_UID_STATE);
+        public long getLastAccessTime(@OpFlags int flags) {
+            return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE,
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Return the last wall clock time this op was accessed by the app while in the foreground.
+         * 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() {
-            return maxTime(mTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        public long getLastAccessForegroundTime(@OpFlags int flags) {
+            return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE,
+                    resolveFirstUnrestrictedUidState(mOp), flags);
         }
 
         /**
-         * Return the last wall clock time this op was accessed by the app while in the background.
+         * 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() {
-            return maxTime(mTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        public long getLastAccessBackgroundTime(@OpFlags int flags) {
+            return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mOp),
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * @hide
+         * 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 getLastTimeFor(int uidState) {
-            return mTimes[uidState];
+        public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            return maxForFlagsInStates(mAccessTimes, fromUidState, toUidState, flags);
         }
 
         /**
          * @hide
          */
         public long getRejectTime() {
-            return maxTime(mRejectTimes, 0, _NUM_UID_STATE);
+            return getLastRejectTime(OP_FLAGS_ALL);
         }
 
         /**
-         * Return the last wall clock time the app made an attempt to access this op but
-         * was rejected.
+         * 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() {
-            return maxTime(mRejectTimes, 0, _NUM_UID_STATE);
+        public long getLastRejectTime(@OpFlags int flags) {
+            return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE,
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Return the last wall clock time the app made an attempt to access this op while in
-         * the foreground but was rejected.
+         * 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() {
-            return maxTime(mRejectTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        public long getLastRejectForegroundTime(@OpFlags int flags) {
+            return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE,
+                    resolveFirstUnrestrictedUidState(mOp), flags);
         }
 
         /**
-         * Return the last wall clock time the app made an attempt to access this op while in
-         * the background but was rejected.
+         * 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() {
-            return maxTime(mRejectTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        public long getLastRejectBackgroundTime(@OpFlags int flags) {
+            return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mOp),
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * @hide
+         * 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 getLastRejectTimeFor(int uidState) {
-            return mRejectTimes[uidState];
+        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;
         }
 
-        public int getDuration() {
-            return mDuration;
+        /**
+         * @return The duration of the operation in milliseconds.
+         */
+        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.
+         *
+         * @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(mOp), flags);
+        }
+
+        /**
+         * Return the duration in milliseconds the app accessed this op 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 background access duration in milliseconds.
+         *
+         * @see #getLastForegroundDuration(int)
+         * @see #getLastDuration(int, int, int)
+         */
+        public long getLastBackgroundDuration(@OpFlags int flags) {
+            return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mOp),
+                    MIN_PRIORITY_UID_STATE, flags);
+        }
+
+        /**
+         * Return the duration in milliseconds the app accessed this op 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 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  mProxyUid;
+            return (int) findFirstNonNegativeForFlagsInStates(mDurations,
+                    MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
         }
 
-        public String getProxyPackageName() {
-            return mProxyPackageName;
+        /**
+         * 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(mDurations,
+                    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 proxy package name.
+         */
+        public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) {
+            return findFirstNonNullForFlagsInStates(mProxyPackageNames, uidState, uidState, flags);
         }
 
         @Override
@@ -2173,23 +2676,23 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mOp);
             dest.writeInt(mMode);
-            dest.writeLongArray(mTimes);
-            dest.writeLongArray(mRejectTimes);
-            dest.writeInt(mDuration);
             dest.writeBoolean(mRunning);
-            dest.writeInt(mProxyUid);
-            dest.writeString(mProxyPackageName);
+            writeLongSparseLongArrayToParcel(mAccessTimes, dest);
+            writeLongSparseLongArrayToParcel(mRejectTimes, dest);
+            writeLongSparseLongArrayToParcel(mDurations, dest);
+            writeLongSparseLongArrayToParcel(mProxyUids, dest);
+            writeLongSparseStringArrayToParcel(mProxyPackageNames, dest);
         }
 
         OpEntry(Parcel source) {
             mOp = source.readInt();
             mMode = source.readInt();
-            mTimes = source.createLongArray();
-            mRejectTimes = source.createLongArray();
-            mDuration = source.readInt();
             mRunning = source.readBoolean();
-            mProxyUid = source.readInt();
-            mProxyPackageName = source.readString();
+            mAccessTimes = readLongSparseLongArrayFromParcel(source);
+            mRejectTimes = readLongSparseLongArrayFromParcel(source);
+            mDurations = readLongSparseLongArrayFromParcel(source);
+            mProxyUids = readLongSparseLongArrayFromParcel(source);
+            mProxyPackageNames = readLongSparseStringArrayFromParcel(source);
         }
 
         public static final @android.annotation.NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
@@ -2226,14 +2729,17 @@
         private final @Nullable List<String> mOpNames;
         private final long mBeginTimeMillis;
         private final long mEndTimeMillis;
+        private final @OpFlags int mFlags;
 
         private HistoricalOpsRequest(int uid, @Nullable String packageName,
-                @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis) {
+                @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
+                @OpFlags int flags) {
             mUid = uid;
             mPackageName = packageName;
             mOpNames = opNames;
             mBeginTimeMillis = beginTimeMillis;
             mEndTimeMillis = endTimeMillis;
+            mFlags = flags;
         }
 
         /**
@@ -2249,6 +2755,7 @@
             private @Nullable List<String> mOpNames;
             private final long mBeginTimeMillis;
             private final long mEndTimeMillis;
+            private @OpFlags int mFlags = OP_FLAGS_ALL;
 
             /**
              * Creates a new builder.
@@ -2311,11 +2818,28 @@
             }
 
             /**
+             * Sets the op flags to query for. The flags specify the type of
+             * op data being queried.
+             *
+             * @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 This builder.
+             */
+            public @NonNull Builder setFlags(@OpFlags int flags) {
+                Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
+                mFlags = flags;
+                return this;
+            }
+
+            /**
              * @return a new {@link HistoricalOpsRequest}.
              */
             public @NonNull HistoricalOpsRequest build() {
                 return new HistoricalOpsRequest(mUid, mPackageName, mOpNames,
-                        mBeginTimeMillis, mEndTimeMillis);
+                        mBeginTimeMillis, mEndTimeMillis, mFlags);
             }
         }
     }
@@ -2521,25 +3045,25 @@
         /** @hide */
         @TestApi
         public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
-                @UidState int uidState, long increment) {
+                @UidState int uidState,  @OpFlags int flags, long increment) {
             getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
-                    packageName, uidState, increment);
+                    packageName, uidState, flags, increment);
         }
 
         /** @hide */
         @TestApi
         public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
-                @UidState int uidState, long increment) {
+                @UidState int uidState, @OpFlags int flags, long increment) {
             getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
-                    packageName, uidState, increment);
+                    packageName, uidState, flags, increment);
         }
 
         /** @hide */
         @TestApi
         public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
-                @UidState int uidState, long increment) {
+                @UidState int uidState, @OpFlags int flags, long increment) {
             getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
-                    packageName, uidState, increment);
+                    packageName, uidState, flags, increment);
         }
 
         /** @hide */
@@ -2834,21 +3358,21 @@
         }
 
         private void increaseAccessCount(int opCode, @NonNull String packageName,
-                @UidState int uidState, long increment) {
+                @UidState int uidState, @OpFlags int flags, long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
-                    opCode, uidState, increment);
+                    opCode, uidState, flags, increment);
         }
 
         private void increaseRejectCount(int opCode, @NonNull String packageName,
-                @UidState int uidState, long increment) {
+                @UidState int uidState,  @OpFlags int flags, long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
-                    opCode, uidState, increment);
+                    opCode, uidState, flags, increment);
         }
 
         private void increaseAccessDuration(int opCode, @NonNull String packageName,
-                @UidState int uidState, long increment) {
+                @UidState int uidState, @OpFlags int flags, long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
-                    opCode, uidState, increment);
+                    opCode, uidState, flags, increment);
         }
 
         /**
@@ -3070,16 +3594,19 @@
             return true;
         }
 
-        private void increaseAccessCount(int opCode, @UidState int uidState, long increment) {
-            getOrCreateHistoricalOp(opCode).increaseAccessCount(uidState, increment);
+        private void increaseAccessCount(int opCode, @UidState int uidState,
+                @OpFlags int flags, long increment) {
+            getOrCreateHistoricalOp(opCode).increaseAccessCount(uidState, flags, increment);
         }
 
-        private void increaseRejectCount(int opCode, @UidState int uidState, long increment) {
-            getOrCreateHistoricalOp(opCode).increaseRejectCount(uidState, increment);
+        private void increaseRejectCount(int opCode, @UidState int uidState,
+                @OpFlags int flags, long increment) {
+            getOrCreateHistoricalOp(opCode).increaseRejectCount(uidState, flags, increment);
         }
 
-        private void increaseAccessDuration(int opCode, @UidState int uidState, long increment) {
-            getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, increment);
+        private void increaseAccessDuration(int opCode, @UidState int uidState,
+                @OpFlags int flags, long increment) {
+            getOrCreateHistoricalOp(opCode).increaseAccessDuration(uidState, flags, increment);
         }
 
         /**
@@ -3095,7 +3622,6 @@
          * Gets number historical app ops.
          *
          * @return The number historical app ops.
-         *
          * @see #getOpAt(int)
          */
         public int getOpCount() {
@@ -3109,9 +3635,7 @@
          * Gets the historical op at a given index.
          *
          * @param index The index to lookup.
-         *
          * @return The op at the given index.
-         *
          * @see #getOpCount()
          */
         public @NonNull HistoricalOp getOpAt(int index) {
@@ -3125,7 +3649,6 @@
          * Gets the historical entry for a given op name.
          *
          * @param opName The op name.
-         *
          * @return The historical entry for that op name.
          */
         public @Nullable HistoricalOp getOp(@NonNull String opName) {
@@ -3219,39 +3742,33 @@
     @SystemApi
     public static final class HistoricalOp implements Parcelable {
         private final int mOp;
-        private @Nullable long[] mAccessCount;
-        private @Nullable long[] mRejectCount;
-        private @Nullable long[] mAccessDuration;
+        private @Nullable LongSparseLongArray mAccessCount;
+        private @Nullable LongSparseLongArray mRejectCount;
+        private @Nullable LongSparseLongArray mAccessDuration;
 
         /** @hide */
         public HistoricalOp(int op) {
             mOp = op;
-            mAccessCount = new long[_NUM_UID_STATE];
-            mRejectCount = new long[_NUM_UID_STATE];
-            mAccessDuration = new long[_NUM_UID_STATE];
         }
 
         private HistoricalOp(@NonNull HistoricalOp other) {
             mOp = other.mOp;
             if (other.mAccessCount != null) {
-                System.arraycopy(other.mAccessCount, 0, getOrCreateAccessCount(),
-                        0, other.mAccessCount.length);
+                mAccessCount = other.mAccessCount.clone();
             }
             if (other.mRejectCount != null) {
-                System.arraycopy(other.mRejectCount, 0, getOrCreateRejectCount(),
-                        0, other.mRejectCount.length);
+                mRejectCount = other.mRejectCount.clone();
             }
             if (other.mAccessDuration != null) {
-                System.arraycopy(other.mAccessDuration, 0, getOrCreateAccessDuration(),
-                        0, other.mAccessDuration.length);
+                mAccessDuration = other.mAccessDuration.clone();
             }
         }
 
         private HistoricalOp(@NonNull Parcel parcel) {
             mOp = parcel.readInt();
-            mAccessCount = parcel.createLongArray();
-            mRejectCount = parcel.createLongArray();
-            mAccessDuration = parcel.createLongArray();
+            mAccessCount = readLongSparseLongArrayFromParcel(parcel);
+            mRejectCount = readLongSparseLongArrayFromParcel(parcel);
+            mAccessDuration = readLongSparseLongArrayFromParcel(parcel);
         }
 
         private void filter(double scaleFactor) {
@@ -3266,90 +3783,64 @@
                     && !hasData(mAccessDuration);
         }
 
-        private boolean hasData(@NonNull long[] array) {
-            for (long value : array) {
-                if (value != 0) {
-                    return true;
-                }
-            }
-            return false;
+        private boolean hasData(@NonNull LongSparseLongArray array) {
+            return (array != null && array.size() > 0);
         }
 
         private @Nullable HistoricalOp splice(double fractionToRemove) {
-            HistoricalOp splice = null;
-            if (mAccessCount != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    final long spliceAccessCount = Math.round(
-                            mAccessCount[i] * fractionToRemove);
-                    if (spliceAccessCount > 0) {
-                        if (splice == null) {
-                            splice = new HistoricalOp(mOp);
-                        }
-                        splice.getOrCreateAccessCount()[i] = spliceAccessCount;
-                        mAccessCount[i] -= spliceAccessCount;
-                    }
-                }
-            }
-
-            if (mRejectCount != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    final long spliceRejectCount = Math.round(
-                            mRejectCount[i] * fractionToRemove);
-
-                    if (spliceRejectCount > 0) {
-                        if (splice == null) {
-                            splice = new HistoricalOp(mOp);
-                        }
-                        splice.getOrCreateRejectCount()[i] = spliceRejectCount;
-                        mRejectCount[i] -= spliceRejectCount;
-                    }
-                }
-            }
-
-            if (mAccessDuration != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    final long spliceAccessDuration =  Math.round(
-                            mAccessDuration[i] * fractionToRemove);
-                    if (spliceAccessDuration > 0) {
-                        if (splice == null) {
-                            splice = new HistoricalOp(mOp);
-                        }
-                        splice.getOrCreateAccessDuration()[i] = spliceAccessDuration;
-                        mAccessDuration[i] -= spliceAccessDuration;
-                    }
-                }
-            }
+            final HistoricalOp splice = new HistoricalOp(mOp);
+            splice(mAccessCount, splice::getOrCreateAccessCount, fractionToRemove);
+            splice(mRejectCount, splice::getOrCreateRejectCount, fractionToRemove);
+            splice(mAccessDuration, splice::getOrCreateAccessDuration, fractionToRemove);
             return splice;
         }
 
+        private static void splice(@Nullable LongSparseLongArray sourceContainer,
+                @NonNull Supplier<LongSparseLongArray> destContainerProvider,
+                    double fractionToRemove) {
+            if (sourceContainer != null) {
+                final int size = sourceContainer.size();
+                for (int i = 0; i < size; i++) {
+                    final long key = sourceContainer.keyAt(i);
+                    final long value = sourceContainer.valueAt(i);
+                    final long removedFraction = Math.round(value * fractionToRemove);
+                    if (removedFraction > 0) {
+                        destContainerProvider.get().put(key, removedFraction);
+                        sourceContainer.put(key, value - removedFraction);
+                    }
+                }
+            }
+        }
+
         private void merge(@NonNull HistoricalOp other) {
-            if (other.mAccessCount != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    getOrCreateAccessCount()[i] += other.mAccessCount[i];
-                }
-            }
-            if (other.mRejectCount != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    getOrCreateRejectCount()[i] += other.mRejectCount[i];
-                }
-            }
-            if (other.mAccessDuration != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    getOrCreateAccessDuration()[i] += other.mAccessDuration[i];
-                }
-            }
+            merge(this::getOrCreateAccessCount, other.mAccessCount);
+            merge(this::getOrCreateRejectCount, other.mRejectCount);
+            merge(this::getOrCreateAccessDuration, other.mAccessDuration);
         }
 
-        private void increaseAccessCount(@UidState int uidState, long increment) {
-            getOrCreateAccessCount()[uidState] += increment;
+        private void increaseAccessCount(@UidState int uidState, @OpFlags int flags,
+                long increment) {
+            increaseCount(getOrCreateAccessCount(), uidState, flags, increment);
         }
 
-        private void increaseRejectCount(@UidState int uidState, long increment) {
-            getOrCreateRejectCount()[uidState] += increment;
+        private void increaseRejectCount(@UidState int uidState, @OpFlags int flags,
+                long increment) {
+            increaseCount(getOrCreateRejectCount(), uidState, flags, increment);
         }
 
-        private void increaseAccessDuration(@UidState int uidState, long increment) {
-            getOrCreateAccessDuration()[uidState] += increment;
+        private void increaseAccessDuration(@UidState int uidState, @OpFlags int flags,
+                long increment) {
+            increaseCount(getOrCreateAccessDuration(), uidState, flags, increment);
+        }
+
+        private void increaseCount(@NonNull LongSparseLongArray counts,
+                @UidState int uidState, @OpFlags int flags, long increment) {
+            while (flags != 0) {
+                final int flag = 1 << Integer.numberOfTrailingZeros(flags);
+                flags &= ~flag;
+                final long key = makeKey(uidState, flag);
+                counts.put(key, counts.get(key) + increment);
+            }
         }
 
         /**
@@ -3369,154 +3860,186 @@
         /**
          * Gets the number times the op was accessed (performed) 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 times the op was accessed in the foreground.
          *
-         * @see #getBackgroundAccessCount()
-         * @see #getAccessCount(int)
+         * @see #getBackgroundAccessCount(int)
+         * @see #getAccessCount(int, int, int)
          */
-        public long getForegroundAccessCount() {
-            if (mAccessCount == null) {
-                return 0;
-            }
-            return sum(mAccessCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        public long getForegroundAccessCount(@OpFlags int flags) {
+            return sumForFlagsInStates(mAccessCount, MAX_PRIORITY_UID_STATE,
+                    resolveFirstUnrestrictedUidState(mOp), flags);
         }
 
         /**
          * Gets the number times the op was accessed (performed) 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 times the op was accessed in the background.
          *
-         * @see #getForegroundAccessCount()
-         * @see #getAccessCount(int)
+         * @see #getForegroundAccessCount(int)
+         * @see #getAccessCount(int, int, int)
          */
-        public long getBackgroundAccessCount() {
-            if (mAccessCount == null) {
-                return 0;
-            }
-            return sum(mAccessCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        public long getBackgroundAccessCount(@OpFlags int flags) {
+            return sumForFlagsInStates(mAccessCount, resolveLastRestrictedUidState(mOp),
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Gets the number times the op was accessed (performed) for a given uid state.
+         * Gets the number times the op was accessed (performed) for a
+         * range of uid states.
          *
-         * @param uidState The UID state for which to query. Could be one of
+         * @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_LOCATION},
          * {@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 times the op was accessed for the given UID state.
          *
-         * @see #getForegroundAccessCount()
-         * @see #getBackgroundAccessCount()
+         * @see #getForegroundAccessCount(int)
+         * @see #getBackgroundAccessCount(int)
          */
-        public long getAccessCount(@UidState int uidState) {
-            if (mAccessCount == null) {
-                return 0;
-            }
-            return mAccessCount[uidState];
+        public long getAccessCount(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            return sumForFlagsInStates(mAccessCount, fromUidState, toUidState, flags);
         }
 
         /**
          * Gets the number times the op was rejected 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 times the op was rejected in the foreground.
          *
-         * @see #getBackgroundRejectCount()
-         * @see #getRejectCount(int)
+         * @see #getBackgroundRejectCount(int)
+         * @see #getRejectCount(int, int, int)
          */
-        public long getForegroundRejectCount() {
-            if (mRejectCount == null) {
-                return 0;
-            }
-            return sum(mRejectCount, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        public long getForegroundRejectCount(@OpFlags int flags) {
+            return sumForFlagsInStates(mRejectCount, MAX_PRIORITY_UID_STATE,
+                    resolveFirstUnrestrictedUidState(mOp), flags);
         }
 
         /**
          * Gets the number times the op was rejected 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 times the op was rejected in the background.
          *
-         * @see #getForegroundRejectCount()
-         * @see #getRejectCount(int)
+         * @see #getForegroundRejectCount(int)
+         * @see #getRejectCount(int, int, int)
          */
-        public long getBackgroundRejectCount() {
-            if (mRejectCount == null) {
-                return 0;
-            }
-            return sum(mRejectCount, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        public long getBackgroundRejectCount(@OpFlags int flags) {
+            return sumForFlagsInStates(mRejectCount, resolveLastRestrictedUidState(mOp),
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Gets the number times the op was rejected for a given uid state.
+         * Gets the number times the op was rejected for a given range of UID states.
          *
-         * @param uidState The UID state for which to query. Could be one of
+         * @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_LOCATION},
          * {@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 times the op was rejected for the given UID state.
          *
-         * @see #getForegroundRejectCount()
-         * @see #getBackgroundRejectCount()
+         * @see #getForegroundRejectCount(int)
+         * @see #getBackgroundRejectCount(int)
          */
-        public long getRejectCount(@UidState int uidState) {
-            if (mRejectCount == null) {
-                return 0;
-            }
-            return mRejectCount[uidState];
+        public long getRejectCount(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            return sumForFlagsInStates(mRejectCount, fromUidState, toUidState, flags);
         }
 
         /**
          * Gets the total duration the app op was accessed (performed) 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 total duration the app op was accessed in the foreground.
          *
-         * @see #getBackgroundAccessDuration()
-         * @see #getAccessDuration(int)
+         * @see #getBackgroundAccessDuration(int)
+         * @see #getAccessDuration(int, int, int)
          */
-        public long getForegroundAccessDuration() {
-            if (mAccessDuration == null) {
-                return 0;
-            }
-            return sum(mAccessDuration, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
+        public long getForegroundAccessDuration(@OpFlags int flags) {
+            return sumForFlagsInStates(mAccessDuration, MAX_PRIORITY_UID_STATE,
+                    resolveFirstUnrestrictedUidState(mOp), flags);
         }
 
         /**
          * Gets the total duration the app op was accessed (performed) 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 total duration the app op was accessed in the background.
          *
-         * @see #getForegroundAccessDuration()
-         * @see #getAccessDuration(int)
+         * @see #getForegroundAccessDuration(int)
+         * @see #getAccessDuration(int, int, int)
          */
-        public long getBackgroundAccessDuration() {
-            if (mAccessDuration == null) {
-                return 0;
-            }
-            return sum(mAccessDuration, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
+        public long getBackgroundAccessDuration(@OpFlags int flags) {
+            return sumForFlagsInStates(mAccessDuration, resolveLastRestrictedUidState(mOp),
+                    MIN_PRIORITY_UID_STATE, flags);
         }
 
         /**
-         * Gets the total duration the app op was accessed (performed) for a given UID state.
+         * Gets the total duration the app op was accessed (performed) for a given
+         * range of UID states.
          *
-         * @param uidState The UID state for which to query. Could be one of
+         * @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_LOCATION},
          * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
          * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+         * @param toUidState The UID state from 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 total duration the app op was accessed for the given UID state.
          *
-         * @see #getForegroundAccessDuration()
-         * @see #getBackgroundAccessDuration()
+         * @see #getForegroundAccessDuration(int)
+         * @see #getBackgroundAccessDuration(int)
          */
-        public long getAccessDuration(@UidState int uidState) {
-            if (mAccessDuration == null) {
-                return 0;
-            }
-            return mAccessDuration[uidState];
+        public long getAccessDuration(@UidState int fromUidState, @UidState int toUidState,
+                @OpFlags int flags) {
+            return sumForFlagsInStates(mAccessDuration, fromUidState, toUidState, flags);
         }
 
         @Override
@@ -3527,80 +4050,11 @@
         @Override
         public void writeToParcel(Parcel parcel, int flags) {
             parcel.writeInt(mOp);
-            parcel.writeLongArray(mAccessCount);
-            parcel.writeLongArray(mRejectCount);
-            parcel.writeLongArray(mAccessDuration);
+            writeLongSparseLongArrayToParcel(mAccessCount, parcel);
+            writeLongSparseLongArrayToParcel(mRejectCount, parcel);
+            writeLongSparseLongArrayToParcel(mAccessDuration, parcel);
         }
 
-        private void accept(@NonNull HistoricalOpsVisitor visitor) {
-            visitor.visitHistoricalOp(this);
-        }
-
-        private @NonNull long[] getOrCreateAccessCount() {
-            if (mAccessCount == null) {
-                mAccessCount = new long[_NUM_UID_STATE];
-            }
-            return mAccessCount;
-        }
-
-        private @NonNull long[] getOrCreateRejectCount() {
-            if (mRejectCount == null) {
-                mRejectCount = new long[_NUM_UID_STATE];
-            }
-            return mRejectCount;
-        }
-
-        private @NonNull long[] getOrCreateAccessDuration() {
-            if (mAccessDuration == null) {
-                mAccessDuration = new long[_NUM_UID_STATE];
-            }
-            return mAccessDuration;
-        }
-
-        /**
-         *
-         * Computes the sum given the start and end index.
-         *
-         * @param counts The data array.
-         * @param start The start index (inclusive)
-         * @param end The end index (exclusive)
-         * @return The sum.
-         */
-        private static long sum(@NonNull long[] counts, int start, int end) {
-            long totalCount = 0;
-            for (int i = start; i < end; i++) {
-                totalCount += counts[i];
-            }
-            return totalCount;
-        }
-
-        /**
-         * Multiplies the entries in the array with the passed in scale factor and
-         * rounds the result at up 0.5 boundary.
-         *
-         * @param data The data to scale.
-         * @param scaleFactor The scale factor.
-         */
-        private static void scale(@NonNull long[] data, double scaleFactor) {
-            if (data != null) {
-                for (int i = 0; i < _NUM_UID_STATE; i++) {
-                    data[i] = (long) HistoricalOps.round((double) data[i] * scaleFactor);
-                }
-            }
-        }
-
-        public static final @android.annotation.NonNull Creator<HistoricalOp> CREATOR = new Creator<HistoricalOp>() {
-            @Override
-            public @NonNull HistoricalOp createFromParcel(@NonNull Parcel source) {
-                return new HistoricalOp(source);
-            }
-
-            @Override
-            public @NonNull HistoricalOp[] newArray(int size) {
-                return new HistoricalOp[size];
-            }
-        };
-
         @Override
         public boolean equals(Object obj) {
             if (this == obj) {
@@ -3613,23 +4067,201 @@
             if (mOp != other.mOp) {
                 return false;
             }
-            if (!Arrays.equals(mAccessCount, other.mAccessCount)) {
+            if (!Objects.equals(mAccessCount, other.mAccessCount)) {
                 return false;
             }
-            if (!Arrays.equals(mRejectCount, other.mRejectCount)) {
+            if (!Objects.equals(mRejectCount, other.mRejectCount)) {
                 return false;
             }
-            return Arrays.equals(mAccessDuration, other.mAccessDuration);
+            return Objects.equals(mAccessDuration, other.mAccessDuration);
         }
 
         @Override
         public int hashCode() {
             int result = mOp;
-            result = 31 * result + Arrays.hashCode(mAccessCount);
-            result = 31 * result + Arrays.hashCode(mRejectCount);
-            result = 31 * result + Arrays.hashCode(mAccessDuration);
+            result = 31 * result + Objects.hashCode(mAccessCount);
+            result = 31 * result + Objects.hashCode(mRejectCount);
+            result = 31 * result + Objects.hashCode(mAccessDuration);
             return result;
         }
+
+        private void accept(@NonNull HistoricalOpsVisitor visitor) {
+            visitor.visitHistoricalOp(this);
+        }
+
+        private @NonNull LongSparseLongArray getOrCreateAccessCount() {
+            if (mAccessCount == null) {
+                mAccessCount = new LongSparseLongArray();
+            }
+            return mAccessCount;
+        }
+
+        private @NonNull LongSparseLongArray getOrCreateRejectCount() {
+            if (mRejectCount == null) {
+                mRejectCount = new LongSparseLongArray();
+            }
+            return mRejectCount;
+        }
+
+        private @NonNull LongSparseLongArray getOrCreateAccessDuration() {
+            if (mAccessDuration == null) {
+                mAccessDuration = new LongSparseLongArray();
+            }
+            return mAccessDuration;
+        }
+
+        /**
+         * Multiplies the entries in the array with the passed in scale factor and
+         * rounds the result at up 0.5 boundary.
+         *
+         * @param data The data to scale.
+         * @param scaleFactor The scale factor.
+         */
+        private static void scale(@NonNull LongSparseLongArray data, double scaleFactor) {
+            if (data != null) {
+                final int size = data.size();
+                for (int i = 0; i < size; i++) {
+                    data.put(data.keyAt(i), (long) HistoricalOps.round(
+                            (double) data.valueAt(i) * scaleFactor));
+                }
+            }
+        }
+
+        /**
+         * Merges two arrays while lazily acquiring the destination.
+         *
+         * @param thisSupplier The destination supplier.
+         * @param other The array to merge in.
+         */
+        private static void merge(@NonNull Supplier<LongSparseLongArray> thisSupplier,
+                @Nullable LongSparseLongArray other) {
+            if (other != null) {
+                final int otherSize = other.size();
+                for (int i = 0; i < otherSize; i++) {
+                    final LongSparseLongArray that = thisSupplier.get();
+                    final long otherKey = other.keyAt(i);
+                    final long otherValue = other.valueAt(i);
+                    that.put(otherKey, that.get(otherKey) + otherValue);
+                }
+            }
+        }
+
+        /** @hide */
+        public @Nullable LongSparseArray<Object> collectKeys() {
+            LongSparseArray<Object> result = AppOpsManager.collectKeys(mAccessCount,
+                null /*result*/);
+            result = AppOpsManager.collectKeys(mRejectCount, result);
+            result = AppOpsManager.collectKeys(mAccessDuration, result);
+            return result;
+        }
+
+        public static final @android.annotation.NonNull Creator<HistoricalOp> CREATOR =
+                new Creator<HistoricalOp>() {
+            @Override
+            public @NonNull HistoricalOp createFromParcel(@NonNull Parcel source) {
+                return new HistoricalOp(source);
+            }
+
+            @Override
+            public @NonNull HistoricalOp[] newArray(int size) {
+                return new HistoricalOp[size];
+            }
+        };
+    }
+
+    /**
+     * Computes the sum of the counts for the given flags in between the begin and
+     * end UID states.
+     *
+     * @param counts The data array.
+     * @param beginUidState The beginning UID state (exclusive).
+     * @param endUidState The end UID state.
+     * @param flags The UID flags.
+     * @return The sum.
+     */
+    private static long sumForFlagsInStates(@Nullable LongSparseLongArray counts,
+            @UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
+        if (counts == null) {
+            return 0;
+        }
+        long sum = 0;
+        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);
+                sum += counts.get(key);
+            }
+        }
+        return sum;
+    }
+
+    /**
+     * Finds the first non-negative value for the given flags in between the begin and
+     * end UID states.
+     *
+     * @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 non-negative value or -1.
+     */
+    private static long findFirstNonNegativeForFlagsInStates(@Nullable LongSparseLongArray counts,
+            @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) {
+        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 flags The UID flags.
+     * @param beginUidState The beginning UID state (exclusive).
+     * @param endUidState The end UID state.
+     * @return The non-negative value or -1.
+     */
+    private static @Nullable String findFirstNonNullForFlagsInStates(
+            @Nullable LongSparseArray<String> counts, @OpFlags int flags,
+            @UidState int beginUidState, @UidState int endUidState) {
+        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;
     }
 
     /**
@@ -3800,7 +4432,7 @@
         Preconditions.checkNotNull(callback, "callback cannot be null");
         try {
             mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames,
-                    request.mBeginTimeMillis, request.mEndTimeMillis,
+                    request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
                     new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
@@ -3840,7 +4472,7 @@
         try {
             mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
                     request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis,
-                    new RemoteCallback((result) -> {
+                    request.mFlags, new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
                 try {
@@ -4310,7 +4942,20 @@
      * Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op.
      * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
      */
-    public int unsafeCheckOpRaw(String op, int uid, String packageName) {
+    public int unsafeCheckOpRaw(@NonNull String op, int uid, String packageName) {
+        try {
+            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Like {@link #unsafeCheckOpNoThrow(String, int, String)} but returns the <em>raw</em>
+     * mode associated with the op. Does not throw a security exception, does not translate
+     * {@link #MODE_FOREGROUND}.
+     */
+    public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
         try {
             return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
         } catch (RemoteException e) {
@@ -4899,16 +5544,110 @@
     }
 
     /**
-     * @hide
+     * Computes the max for the given flags in between the begin and
+     * end UID states.
+     *
+     * @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.
      */
-    public static long maxTime(long[] times, int start, int end) {
-        long time = 0;
-        for (int i = start; i < end; i++) {
-            if (times[i] > time) {
-                time = times[i];
+    private static long maxForFlagsInStates(@Nullable LongSparseLongArray counts,
+            @UidState int beginUidState, @UidState int endUidState,
+            @OpFlags int flags) {
+        if (counts == null) {
+            return 0;
+        }
+        long max = 0;
+        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);
+                max = Math.max(max, counts.get(key));
             }
         }
-        return time;
+        return max;
+    }
+
+
+    private static void writeLongSparseLongArrayToParcel(
+            @Nullable LongSparseLongArray 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.writeLong(array.valueAt(i));
+            }
+        } else {
+            parcel.writeInt(-1);
+        }
+    }
+
+    private static @Nullable LongSparseLongArray readLongSparseLongArrayFromParcel(
+            @NonNull Parcel parcel) {
+        final int size = parcel.readInt();
+        if (size < 0) {
+            return null;
+        }
+        final LongSparseLongArray array = new LongSparseLongArray(size);
+        for (int i = 0; i < size; i++) {
+            array.append(parcel.readLong(), parcel.readLong());
+        }
+        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.
+     *
+     * @param array The array whose keys to collect.
+     * @param result The optional result store collected keys.
+     * @return The result collected keys array.
+     */
+    private static LongSparseArray<Object> collectKeys(@Nullable LongSparseLongArray array,
+            @Nullable LongSparseArray<Object> result) {
+        if (array != null) {
+            if (result == null) {
+                result = new LongSparseArray<>();
+            }
+            final int accessSize = array.size();
+            for (int i = 0; i < accessSize; i++) {
+                result.put(array.keyAt(i), null);
+            }
+        }
+        return result;
     }
 
     /** @hide */
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 4a826d1..010a900 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -16,18 +16,15 @@
 
 package android.app;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.NotificationManager.InterruptionFilter;
 import android.content.ComponentName;
-import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.service.notification.ZenPolicy;
 import android.service.notification.Condition;
-
-import com.android.internal.util.Preconditions;
+import android.service.notification.ZenPolicy;
 
 import java.util.Objects;
 
@@ -92,8 +89,9 @@
      *               action ({@link Condition#STATE_TRUE}).
      * @param enabled Whether the rule is enabled.
      */
-    public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
-            Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled) {
+    public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner,
+            @Nullable ComponentName configurationActivity, @NonNull Uri conditionId,
+            @Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) {
         this.name = name;
         this.owner = owner;
         this.configurationActivity = configurationActivity;
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 65859c7..ad614b1 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -1,18 +1,37 @@
 {
-  "imports": [
-    {
-      "path": "frameworks/base/services/core/java/com/android/server/am"
-    },
-    {
-      "path": "frameworks/base/services/core/java/com/android/server/wm"
-    }
-  ],
-  "presubmit": [
-      {
-        "name": "CtsFragmentTestCases"
-      },
-      {
-        "name": "CtsFragmentTestCasesSdk26"
-      }
-  ]
+    "imports": [
+        {
+            "path": "frameworks/base/services/core/java/com/android/server/am"
+        },
+        {
+            "path": "frameworks/base/services/core/java/com/android/server/wm"
+        }
+    ],
+    "presubmit": [
+        {
+            "name": "CtsFragmentTestCases"
+        },
+        {
+            "name": "CtsFragmentTestCasesSdk26"
+        },
+        {
+            "file_patterns": ["(/|^)AppOpsManager.java"],
+            "name": "CtsAppOpsTestCases"
+        },
+        {
+            "file_patterns": ["(/|^)AppOpsManager.java"],
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.appop.AppOpsUpgradeTest"
+                },
+                {
+                    "include-filter": "com.android.server.appop.AppOpsServiceTest"
+                },
+                {
+                    "include-filter": "com.android.server.appop.AppOpsActiveWatcherTest"
+                }
+            ]
+        }
+    ]
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d593ad1..efbd098 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3153,7 +3153,7 @@
      * {@link #PASSWORD_QUALITY_ALPHANUMERIC} with {@link #setPasswordQuality}.
      * <p>
      * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
-     * password is always treated as empty.
+     * password history length is always 0.
      * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
@@ -3191,6 +3191,9 @@
      * <p>
      * To disable password expiration, a value of 0 may be used for timeout.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password expiration is always disabled.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -3230,6 +3233,9 @@
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password expiration is always disabled and this method always returns 0.
+     *
      * @param admin The name of the admin component to check, or {@code null} to aggregate all admins.
      * @return The timeout for the given admin or the minimum of all timeouts
      */
@@ -3255,6 +3261,9 @@
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * the password expiration for the parent profile.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password expiration is always disabled and this method always returns 0.
+     *
      * @param admin The name of the admin component to check, or {@code null} to aggregate all admins.
      * @return The password expiration time, in milliseconds since epoch.
      */
@@ -3279,6 +3288,9 @@
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password history length is always 0.
+     *
      * @param admin The name of the admin component to check, or {@code null} to aggregate
      * all admins.
      * @return The length of the password history
@@ -3306,7 +3318,7 @@
      * Return the maximum password length that the device supports for a
      * particular password quality.
      * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
-     * password is always empty.
+     * password is always empty and this method always returns 0.
      * @param quality The quality being interrogated.
      * @return Returns the maximum length that the user can enter.
      */
@@ -3362,7 +3374,7 @@
      * #getParentProfileInstance}.
      *
      * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
-     * password is always treated as empty.
+     * password is always empty and this method returns {@link #PASSWORD_COMPLEXITY_NONE}.
      *
      * @throws IllegalStateException if the user is not unlocked.
      * @throws SecurityException if the calling application does not have the permission
@@ -3437,6 +3449,8 @@
      * <p>
      * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN}
      * to be able to call this method; if it has not, a security exception will be thrown.
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always empty and this method always returns 0.
      *
      * @return The number of times user has entered an incorrect password since the last correct
      *         password entry.
@@ -3503,6 +3517,8 @@
      * This method can be called on the {@link DevicePolicyManager} instance returned by
      * {@link #getParentProfileInstance(ComponentName)} in order to set a value on the parent
      * profile.
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always empty and this method has no effect - i.e. the policy is not set.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param num The number of failed password attempts at which point the device or profile will
@@ -3532,6 +3548,10 @@
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * the value for the parent profile.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always empty and this method returns a default value (0) indicating that the
+     * policy is not set.
+     *
      * @param admin The name of the admin component to check, or {@code null} to aggregate
      * all admins.
      */
@@ -3619,6 +3639,8 @@
      * {@link android.os.Build.VERSION_CODES#N} and later for managed profiles, or for device admins
      * that are not device owner or profile owner.  Once set, the password cannot be changed to null
      * or empty except by these admins.</em>
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, this
+     * methods does nothing.
      * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call this method; if it has
@@ -3669,6 +3691,8 @@
      * will be stored on your server and who will need access to them. Tokens may be the subject of
      * legal access requests.
      * </em>
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * reset token is not set and this method returns false.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param token a secure token a least 32-byte long, which must be generated by a
@@ -3693,6 +3717,10 @@
     /**
      * Called by a profile or device owner to revoke the current password reset token.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, this
+     * method has no effect - the reset token should not have been set in the first place - and
+     * false is returned.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return true if the operation is successful, false otherwise.
      * @throws SecurityException if admin is not a device or profile owner.
@@ -3713,6 +3741,9 @@
     /**
      * Called by a profile or device owner to check if the current reset password token is active.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature,
+     * false is always returned.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return true if the token is active, false otherwise.
      * @throws SecurityException if admin is not a device or profile owner.
@@ -3748,6 +3779,8 @@
      * <p>
      * Calling with a {@code null} or empty password will clear any existing PIN, pattern or
      * password if the current password constraints allow it.
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature,
+     * calling this methods has no effect - the password is always empty - and false is returned.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param password The new password for the user. {@code null} or empty clears the password.
@@ -3855,6 +3888,9 @@
      * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
      * profile.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature,
+     * calling this methods has no effect - i.e. the timeout is not set.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param timeoutMs The new timeout in milliseconds, after which the user will have to unlock
      *         with strong authentication method. A value of 0 means the admin is not participating
@@ -3887,6 +3923,9 @@
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature,
+     * 0 is returned to indicate that no timeout is configured.
+     *
      * @param admin The name of the admin component to check, or {@code null} to aggregate
      *         across all participating admins.
      * @return The timeout in milliseconds or 0 if not configured for the provided admin.
@@ -6617,6 +6656,9 @@
      * This method can be called on the {@link DevicePolicyManager} instance returned by
      * {@link #getParentProfileInstance(ComponentName)} in order to set the configuration for
      * the parent profile.
+     * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, calling
+     * this method has no effect - no trust agent configuration will be set.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param target Component name of the agent to be configured.
@@ -6646,6 +6688,9 @@
      * This method can be called on the {@link DevicePolicyManager} instance returned by
      * {@link #getParentProfileInstance(ComponentName)} in order to retrieve the configuration set
      * on the parent profile.
+     * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, null is
+     * always returned.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. If null,
      * this function returns a list of configurations for all admins that declare
diff --git a/core/java/android/app/contentsuggestions/ClassificationsRequest.java b/core/java/android/app/contentsuggestions/ClassificationsRequest.java
index 1b50015..2051a55 100644
--- a/core/java/android/app/contentsuggestions/ClassificationsRequest.java
+++ b/core/java/android/app/contentsuggestions/ClassificationsRequest.java
@@ -54,10 +54,10 @@
     }
 
     /**
-     * Return the request extras or {@code null} if there are none.
+     * Return the request extras, can be an empty bundle.
      */
-    public @Nullable Bundle getExtras() {
-        return mExtras;
+    public @NonNull Bundle getExtras() {
+        return mExtras == null ? new Bundle() : mExtras;
     }
 
     @Override
@@ -103,7 +103,7 @@
         /**
          * Sets the request extras.
          */
-        public Builder setExtras(@NonNull Bundle extras) {
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
             mExtras = extras;
             return this;
         }
@@ -111,7 +111,7 @@
         /**
          * Builds a new request instance.
          */
-        public ClassificationsRequest build() {
+        public @NonNull ClassificationsRequest build() {
             return new ClassificationsRequest(mSelections, mExtras);
         }
     }
diff --git a/core/java/android/app/contentsuggestions/SelectionsRequest.java b/core/java/android/app/contentsuggestions/SelectionsRequest.java
index 257f98d..84f33b5 100644
--- a/core/java/android/app/contentsuggestions/SelectionsRequest.java
+++ b/core/java/android/app/contentsuggestions/SelectionsRequest.java
@@ -63,10 +63,10 @@
     }
 
     /**
-     * Return the request extras or {@code null} if there aren't any.
+     * Return the request extras, may be an empty bundle if there aren't any.
      */
-    public @Nullable Bundle getExtras() {
-        return mExtras;
+    public @NonNull Bundle getExtras() {
+        return mExtras == null ? new Bundle() : mExtras;
     }
 
     @Override
@@ -118,7 +118,7 @@
         /**
          * Sets the request extras.
          */
-        public Builder setExtras(@NonNull Bundle extras) {
+        public @NonNull Builder setExtras(@NonNull Bundle extras) {
             mExtras = extras;
             return this;
         }
@@ -126,7 +126,7 @@
         /**
          * Sets the request interest point.
          */
-        public Builder setInterestPoint(@NonNull Point interestPoint) {
+        public @NonNull Builder setInterestPoint(@NonNull Point interestPoint) {
             mInterestPoint = interestPoint;
             return this;
         }
@@ -134,7 +134,7 @@
         /**
          * Builds a new request instance.
          */
-        public SelectionsRequest build() {
+        public @NonNull SelectionsRequest build() {
             return new SelectionsRequest(mTaskId, mInterestPoint, mExtras);
         }
     }
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index b72ec39..62b24e9 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -60,7 +60,7 @@
             in PendingIntent sessionEndCallbackIntent, String callingPackage);
     void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
     void registerAppUsageLimitObserver(int observerId, in String[] packages, long timeLimitMs,
-            in PendingIntent callback, String callingPackage);
+            long timeRemainingMs, in PendingIntent callback, String callingPackage);
     void unregisterAppUsageLimitObserver(int observerId, String callingPackage);
     void reportUsageStart(in IBinder activity, String token, String callingPackage);
     void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index d34e6d3..cee6b87 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -35,6 +35,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -738,6 +739,23 @@
     }
 
     /**
+     * @deprecated use
+     * {@link #registerAppUsageLimitObserver(int, String[], Duration, Duration, PendingIntent)}.
+     *
+     * @removed
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    // STOPSHIP b/126917290: remove this method once ag/6591106 is merged and it's not being used.
+    public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
+            long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) {
+        final Duration timeLimitDuration = Duration.ofMillis(timeUnit.toMillis(timeLimit));
+        registerAppUsageLimitObserver(observerId, observedEntities,
+                timeLimitDuration, timeLimitDuration, callbackIntent);
+    }
+
+    /**
      * Register a usage limit observer that receives a callback on the provided intent when the
      * sum of usages of apps and tokens in the provided {@code observedEntities} array exceeds the
      * {@code timeLimit} specified. The structure of a token is a {@link String} with the reporting
@@ -759,19 +777,21 @@
      * @see android.content.pm.LauncherApps#getAppUsageLimit
      *
      * @param observerId A unique id associated with the group of apps to be monitored. There can
-     *                  be multiple groups with common packages and different time limits.
+     *                   be multiple groups with common packages and different time limits.
      * @param observedEntities The list of packages and token to observe for usage time. Cannot be
      *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be in the foreground before the
-     *                  callbackIntent is delivered. Must be at least one minute. Note: a limit of
-     *                  0 can be set to indicate that the user has already exhausted the limit for
-     *                  a group, in which case, the given {@code callbackIntent} will be ignored.
-     * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+     *                  {@code callbackIntent} is delivered. Must be at least one minute.
+     * @param timeRemaining The remaining time the set of apps can be in the foreground before the
+     *                      {@code callbackIntent} is delivered. Must be greater than
+     *                      {@code timeLimit}. Note: a limit of 0 can be set to indicate that the
+     *                      user has already exhausted the limit for a group, in which case,
+     *                      the given {@code callbackIntent} will be ignored.
      * @param callbackIntent The PendingIntent that will be dispatched when the usage limit is
      *                       exceeded by the group of apps. The delivered Intent will also contain
      *                       the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
      *                       {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is
-     *                       being registered with a {@code timeLimit} of 0.
+     *                       being registered with a {@code timeRemaining} of 0.
      * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
      *                           permissions.
      * @hide
@@ -781,10 +801,12 @@
             android.Manifest.permission.SUSPEND_APPS,
             android.Manifest.permission.OBSERVE_APP_USAGE})
     public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
-            long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) {
+            @NonNull Duration timeLimit, @NonNull Duration timeRemaining,
+            @Nullable PendingIntent callbackIntent) {
         try {
             mService.registerAppUsageLimitObserver(observerId, observedEntities,
-                    timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
+                    timeLimit.toMillis(), timeRemaining.toMillis(), callbackIntent,
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/LocusId.java b/core/java/android/content/LocusId.java
index 2142cf3..3d1ddc3 100644
--- a/core/java/android/content/LocusId.java
+++ b/core/java/android/content/LocusId.java
@@ -16,7 +16,6 @@
 package android.content;
 
 import android.annotation.NonNull;
-import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -34,28 +33,28 @@
 // TODO(b/123577059): make sure this is well documented and understandable
 public final class LocusId implements Parcelable {
 
-    private final Uri mUri;
+    private final String mId;
 
     /**
      * Default constructor.
      */
-    public LocusId(@NonNull Uri uri) {
-        mUri = Preconditions.checkNotNull(uri);
+    public LocusId(@NonNull String id) {
+        mId = Preconditions.checkNotNull(id);
     }
 
     /**
-     * Gets the {@code uri} associated with the locus.
+     * Gets the {@code id} associated with the locus.
      */
     @NonNull
-    public Uri getUri() {
-        return mUri;
+    public String getId() {
+        return mId;
     }
 
     @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((mUri == null) ? 0 : mUri.hashCode());
+        result = prime * result + ((mId == null) ? 0 : mId.hashCode());
         return result;
     }
 
@@ -65,26 +64,27 @@
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
         final LocusId other = (LocusId) obj;
-        if (mUri == null) {
-            if (other.mUri != null) return false;
+        if (mId == null) {
+            if (other.mId != null) return false;
         } else {
-            if (!mUri.equals(other.mUri)) return false;
+            if (!mId.equals(other.mId)) return false;
         }
         return true;
     }
 
     @Override
     public String toString() {
-        return "LocusId[uri=" + getSanitizedUri() + "]";
+        return "LocusId[" + getSanitizedId() + "]";
     }
 
     /** @hide */
     public void dump(@NonNull PrintWriter pw) {
-        pw.print("uri:"); pw.println(getSanitizedUri());
+        pw.print("id:"); pw.println(getSanitizedId());
     }
 
-    private String getSanitizedUri() {
-        final int size = mUri.toString().length();
+    @NonNull
+    private String getSanitizedId() {
+        final int size = mId.length();
         return size + "_chars";
     }
 
@@ -94,8 +94,8 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mUri, flags);
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mId);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<LocusId> CREATOR =
@@ -103,9 +103,8 @@
 
         @NonNull
         @Override
-        public LocusId createFromParcel(Parcel source) {
-            final Uri uri = source.readParcelable(null);
-            return new LocusId(uri);
+        public LocusId createFromParcel(Parcel parcel) {
+            return new LocusId(parcel.readString());
         }
 
         @NonNull
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 38ea43e..0cc5f39 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -792,7 +792,7 @@
      *
      * @return an {@link AppUsageLimit} object describing the app time limit containing
      * the given package with the smallest time remaining, or {@code null} if none exist.
-     * @throws SecurityException when the caller is not the active launcher.
+     * @throws SecurityException when the caller is not the recents app.
      */
     @Nullable
     public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName,
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 0304f19..b20cce9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1343,6 +1343,7 @@
          */
         public boolean areHiddenOptionsSet() {
             return (installFlags & (PackageManager.INSTALL_ALLOW_DOWNGRADE
+                    | PackageManager.INSTALL_RESPECT_ALLOW_DOWNGRADE
                     | PackageManager.INSTALL_DONT_KILL_APP
                     | PackageManager.INSTALL_INSTANT_APP
                     | PackageManager.INSTALL_FULL_APP
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a5464c2..c133fba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -721,6 +721,7 @@
             INSTALL_VIRTUAL_PRELOAD,
             INSTALL_APEX,
             INSTALL_ENABLE_ROLLBACK,
+            INSTALL_RESPECT_ALLOW_DOWNGRADE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallFlags {}
@@ -865,6 +866,15 @@
      */
     public static final int INSTALL_DISABLE_VERIFICATION = 0x00080000;
 
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that
+     * {@link #INSTALL_ALLOW_DOWNGRADE} should be respected.
+     *
+     * @hide
+     */
+    // TODO(b/127322579): rename
+    public static final int INSTALL_RESPECT_ALLOW_DOWNGRADE = 0x00100000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
             DONT_KILL_APP
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a25bbdb..9da8e4e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -85,7 +85,8 @@
          *
          * @hide
          */
-        public Key(String name, String fallbackName, Class<T> type) {
+        @UnsupportedAppUsage
+        public Key(@NonNull String name, @NonNull String fallbackName, @NonNull Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name,  fallbackName, type);
         }
 
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 536c2e1..23401432 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -678,6 +678,31 @@
         public void onCameraUnavailable(@NonNull String cameraId) {
             // default empty implementation
         }
+
+        /**
+         * Notify registered clients about a change in the camera access priorities.
+         *
+         * <p>Notification that camera access priorities have changed and the camera may
+         * now be openable. An application that was previously denied camera access due to
+         * a higher-priority user already using the camera, or that was disconnected from an
+         * active camera session due to a higher-priority user trying to open the camera,
+         * should try to open the camera again if it still wants to use it.  Note that
+         * multiple applications may receive this callback at the same time, and only one of
+         * them will succeed in opening the camera in practice, depending on exact access
+         * priority levels and timing. This method is useful in cases where multiple
+         * applications may be in the resumed state at the same time, and the user switches
+         * focus between them, or if the current camera-using application moves between
+         * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera
+         * available/unavailable callbacks will not be invoked, but another application may
+         * now have higher priority for camera access than the current camera-using
+         * application.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         */
+        public void onCameraAccessPrioritiesChanged() {
+            // default empty implementation
+        }
     }
 
     /**
@@ -1098,6 +1123,22 @@
             }
         }
 
+        private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback,
+                final Executor executor) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                executor.execute(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onCameraAccessPrioritiesChanged();
+                        }
+                    });
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
                 final String id, final int status) {
             if (isAvailable(status)) {
@@ -1347,6 +1388,19 @@
             }
         }
 
+        @Override
+        public void onCameraAccessPrioritiesChanged() {
+            synchronized (mLock) {
+                final int callbackCount = mCallbackMap.size();
+                for (int i = 0; i < callbackCount; i++) {
+                    Executor executor = mCallbackMap.valueAt(i);
+                    final AvailabilityCallback callback = mCallbackMap.keyAt(i);
+
+                    postSingleAccessPriorityChangeUpdate(callback, executor);
+                }
+            }
+        }
+
         /**
          * Try to connect to camera service after some delay if any client registered camera
          * availability callback or torch status callback.
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 53d4dd3..bb0987d 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -89,7 +89,8 @@
          *
          * @hide
          */
-        public Key(String name, String fallbackName, Class<T> type) {
+        @UnsupportedAppUsage
+        public Key(@NonNull String name, @NonNull String fallbackName, @NonNull Class<T> type) {
             mKey = new CameraMetadataNative.Key<T>(name, fallbackName, type);
         }
 
@@ -4251,6 +4252,7 @@
      * @see CaptureResult#SENSOR_TIMESTAMP
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
             new Key<long[]>("android.statistics.oisTimestamps", long[].class);
 
@@ -4270,6 +4272,7 @@
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
             new Key<float[]>("android.statistics.oisXShifts", float[].class);
 
@@ -4289,6 +4292,7 @@
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * @hide
      */
+    @UnsupportedAppUsage
     public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
             new Key<float[]>("android.statistics.oisYShifts", float[].class);
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a17ebcb..eb0fe33 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -143,6 +143,7 @@
             NET_CAPABILITY_NOT_CONGESTED,
             NET_CAPABILITY_NOT_SUSPENDED,
             NET_CAPABILITY_OEM_PAID,
+            NET_CAPABILITY_MCX
     })
     public @interface NetCapability { }
 
@@ -297,8 +298,14 @@
     @SystemApi
     public static final int NET_CAPABILITY_OEM_PAID = 22;
 
+    /**
+     * Indicates this is a network that has the ability to reach a carrier's Mission Critical
+     * servers.
+     */
+    public static final int NET_CAPABILITY_MCX = 23;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PAID;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_MCX;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -346,7 +353,8 @@
             (1 << NET_CAPABILITY_IA) |
             (1 << NET_CAPABILITY_IMS) |
             (1 << NET_CAPABILITY_RCS) |
-            (1 << NET_CAPABILITY_XCAP);
+            (1 << NET_CAPABILITY_XCAP) |
+            (1 << NET_CAPABILITY_MCX);
 
     /**
      * Capabilities that force network to be restricted.
@@ -1614,6 +1622,7 @@
             case NET_CAPABILITY_NOT_CONGESTED:  return "NOT_CONGESTED";
             case NET_CAPABILITY_NOT_SUSPENDED:  return "NOT_SUSPENDED";
             case NET_CAPABILITY_OEM_PAID:       return "OEM_PAID";
+            case NET_CAPABILITY_MCX:            return "MCX";
             default:                            return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 1bdfd94..a1f2430 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -36,6 +36,8 @@
     private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
     private boolean mAll;
     private int mPrivacyPolicy;
+    private String mReceiverPkg;
+    private String mReceiverCls;
 
     /**
      * Construct an incident report args with no fields.
@@ -73,6 +75,10 @@
         }
 
         out.writeInt(mPrivacyPolicy);
+
+        out.writeString(mReceiverPkg);
+
+        out.writeString(mReceiverCls);
     }
 
     public void readFromParcel(Parcel in) {
@@ -91,6 +97,10 @@
         }
 
         mPrivacyPolicy = in.readInt();
+
+        mReceiverPkg = in.readString();
+
+        mReceiverCls = in.readString();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<IncidentReportArgs> CREATOR
@@ -126,6 +136,8 @@
         sb.append(mHeaders.size());
         sb.append(" headers), ");
         sb.append("privacy: ").append(mPrivacyPolicy);
+        sb.append("receiver pkg: ").append(mReceiverPkg);
+        sb.append("receiver cls: ").append(mReceiverCls);
         return sb.toString();
     }
 
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index cb2517e..76e911d 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -40,6 +40,7 @@
     void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
     void isApplicationQualifiedForRole(String roleName, String packageName,
             in RemoteCallback callback);
+    void isRoleVisible(String roleName, in RemoteCallback callback);
     void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
             String permission, int grantState, in RemoteCallback callback);
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 9d58064..5695e42 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -476,6 +476,26 @@
     }
 
     /**
+     * Check whether a role should be visible to user.
+     *
+     * @param roleName name of the role to check for
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+    public void isRoleVisible(@NonNull String roleName,
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+        checkStringNotEmpty(roleName);
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        mRemoteService.scheduleRequest(new PendingIsRoleVisibleRequest(mRemoteService, roleName,
+                executor, callback));
+    }
+
+    /**
      * A connection to the remote service
      */
     static final class RemoteService extends
@@ -1222,4 +1242,55 @@
             }
         }
     }
+
+    /**
+     * Request for {@link #isRoleVisible}.
+     */
+    private static final class PendingIsRoleVisibleRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+
+        private final @NonNull String mRoleName;
+        private final @NonNull Consumer<Boolean> mCallback;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingIsRoleVisibleRequest(@NonNull RemoteService service,
+                @NonNull String roleName, @NonNull @CallbackExecutor Executor executor,
+                @NonNull Consumer<Boolean> callback) {
+            super(service);
+
+            mRoleName = roleName;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    boolean visible;
+                    if (result != null) {
+                        visible = result.getBoolean(KEY_RESULT);
+                    } else {
+                        visible = false;
+                    }
+                    callback.accept(visible);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    finish();
+                }
+            }), null);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mCallback.accept(false);
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().isRoleVisible(mRoleName, mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error checking whether role should be visible", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index ee03689..d375c10 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -179,12 +179,21 @@
      * @param roleName name of the role to check for
      * @param packageName package name of the application to check for
      *
-     * @return whether the application is qualified for the role.
+     * @return whether the application is qualified for the role
      */
     public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
             @NonNull String packageName);
 
     /**
+     * Check whether a role should be visible to user.
+     *
+     * @param roleName name of the role to check for
+     *
+     * @return whether the role should be visible to user
+     */
+    public abstract boolean onIsRoleVisible(@NonNull String roleName);
+
+    /**
      * Set the runtime permission state from a device admin.
      *
      * @param callerPackageName The package name of the admin requesting the change
@@ -344,6 +353,18 @@
             }
 
             @Override
+            public void isRoleVisible(String roleName, RemoteCallback callback) {
+                checkStringNotEmpty(roleName);
+                checkNotNull(callback, "callback");
+
+                enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
+
+                mHandler.sendMessage(obtainMessage(
+                        PermissionControllerService::isRoleVisible,
+                        PermissionControllerService.this, roleName, callback));
+            }
+
+            @Override
             public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName,
                     String packageName, String permission, int grantState,
                     RemoteCallback callback) {
@@ -445,6 +466,13 @@
         callback.sendResult(result);
     }
 
+    private void isRoleVisible(@NonNull String roleName, @NonNull RemoteCallback callback) {
+        boolean visible = onIsRoleVisible(roleName);
+        Bundle result = new Bundle();
+        result.putBoolean(PermissionControllerManager.KEY_RESULT, visible);
+        callback.sendResult(result);
+    }
+
     private void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
             @NonNull String packageName, @NonNull String permission,
             @PermissionGrantState int grantState, @NonNull RemoteCallback callback) {
diff --git a/core/java/android/permission/RuntimePermissionPresentationInfo.java b/core/java/android/permission/RuntimePermissionPresentationInfo.java
index d66789f..4fce14c 100644
--- a/core/java/android/permission/RuntimePermissionPresentationInfo.java
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.java
@@ -21,6 +21,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * This class contains information about how a runtime permission
  * is to be presented in the UI. A single runtime permission
@@ -35,7 +37,7 @@
     private static final int FLAG_GRANTED = 1 << 0;
     private static final int FLAG_STANDARD = 1 << 1;
 
-    private final CharSequence mLabel;
+    private final @NonNull CharSequence mLabel;
     private final int mFlags;
 
     /**
@@ -45,8 +47,10 @@
      * @param granted Whether the permission is granted.
      * @param standard Whether this is a platform-defined permission.
      */
-    public RuntimePermissionPresentationInfo(CharSequence label,
+    public RuntimePermissionPresentationInfo(@NonNull CharSequence label,
             boolean granted, boolean standard) {
+        Preconditions.checkNotNull(label);
+
         mLabel = label;
         int flags = 0;
         if (granted) {
@@ -58,11 +62,6 @@
         mFlags = flags;
     }
 
-    private RuntimePermissionPresentationInfo(Parcel parcel) {
-        mLabel = parcel.readCharSequence();
-        mFlags = parcel.readInt();
-    }
-
     /**
      * @return Whether the permission is granted.
      */
@@ -97,10 +96,14 @@
         parcel.writeInt(mFlags);
     }
 
-    public static final @android.annotation.NonNull Creator<RuntimePermissionPresentationInfo> CREATOR =
+    public static final @NonNull Creator<RuntimePermissionPresentationInfo> CREATOR =
             new Creator<RuntimePermissionPresentationInfo>() {
         public RuntimePermissionPresentationInfo createFromParcel(Parcel source) {
-            return new RuntimePermissionPresentationInfo(source);
+            CharSequence label = source.readCharSequence();
+            int flags = source.readInt();
+
+            return new RuntimePermissionPresentationInfo(label, (flags & FLAG_GRANTED) != 0,
+                    (flags & FLAG_STANDARD) != 0);
         }
 
         public RuntimePermissionPresentationInfo[] newArray(int size) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 2cd3c48..5d4539c 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -64,12 +64,14 @@
     public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
 
     /**
-     * Namespace for all Game Driver features.
+     * Namespace for all activity manager related features that are used at the native level.
+     * These features are applied at reboot.
      *
      * @hide
      */
     @SystemApi
-    public static final String NAMESPACE_GAME_DRIVER = "game_driver";
+    public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT =
+            "activity_manager_native_boot";
 
     /**
      * Namespace for autofill feature that provides suggestions across all apps when
@@ -92,6 +94,14 @@
     public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
 
     /**
+     * Namespace for all Game Driver features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_GAME_DRIVER = "game_driver";
+
+    /**
      * Namespace for all input-related features that are used at the native level.
      * These features are applied at reboot.
      *
@@ -101,6 +111,14 @@
     public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
 
     /**
+     * Namespace for all media native related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
+
+    /**
      * Namespace for all netd related features.
      *
      * @hide
@@ -109,6 +127,15 @@
     public static final String NAMESPACE_NETD_NATIVE = "netd_native";
 
     /**
+     * Namespace for all runtime native boot related features. Boot in this case refers to the
+     * fact that the properties only take affect after rebooting the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
+
+    /**
      * Namespace for System UI related features.
      *
      * @hide
@@ -174,40 +201,6 @@
     }
 
     /**
-     * Namespace for all runtime native boot related features. Boot in this case refers to the
-     * fact that the properties only take affect after rebooting the device.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface RuntimeNativeBoot {
-        String NAMESPACE = "runtime_native_boot";
-    }
-
-    /**
-     * Namespace for all media native related features.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface MediaNative {
-        /** The flag namespace for media native features. */
-        String NAMESPACE = "media_native";
-    }
-
-    /**
-     * Namespace for all activity manager related features that are used at the native level.
-     * These features are applied at reboot.
-     *
-     * @hide
-     */
-    @SystemApi
-    public interface ActivityManagerNativeBoot {
-        String NAMESPACE = "activity_manager_native_boot";
-        String OFFLOAD_QUEUE_ENABLED = "offload_queue_enabled";
-    }
-
-    /**
      * Namespace for attention-based features provided by on-device machine intelligence.
      *
      * @hide
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index a34ac70..917b5c2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -146,6 +146,8 @@
     public static final String RETRANSLATE_CALL = "update_titles";
 
     /** {@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";
@@ -3318,21 +3320,41 @@
     public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
 
     /**
-     * Get the media provider's version.
-     * Applications that import data from the media provider into their own caches
-     * can use this to detect that the media provider changed, and reimport data
-     * as needed. No other assumptions should be made about the meaning of the version.
-     * @param context Context to use for performing the query.
-     * @return A version string, or null if the version could not be determined.
+     * 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};
+     * to obtain a version for a different volume, use
+     * {@link #getVersion(Context, String)}.
      */
-    public static String getVersion(Context context) {
-        final Uri uri = AUTHORITY_URI.buildUpon().appendPath("none").appendPath("version").build();
-        try (Cursor c = context.getContentResolver().query(uri, null, null, null, null)) {
-            if (c.moveToFirst()) {
-                return c.getString(0);
-            }
+    public static @NonNull String getVersion(@NonNull Context context) {
+        return getVersion(context, VOLUME_EXTERNAL);
+    }
+
+    /**
+     * 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.
+     */
+    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 null;
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 368fc2a..45219d6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3321,6 +3321,17 @@
                         ColorDisplayManager.COLOR_MODE_AUTOMATIC);
 
         /**
+         * The user selected peak refresh rate in frames per second.
+         *
+         * If this isn't set, the system falls back to a device specific default.
+         * @hide
+         */
+        public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
+
+        private static final Validator PEAK_REFRESH_RATE_VALIDATOR =
+                new SettingsValidators.InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
+
+        /**
          * The amount of time in milliseconds before the device goes to sleep or begins
          * to dream after a period of inactivity.  This value is also known as the
          * user activity timeout period since the screen isn't necessarily turned off
diff --git a/core/java/android/service/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java
index d53eb37..57e4b1b 100644
--- a/core/java/android/service/carrier/ApnService.java
+++ b/core/java/android/service/carrier/ApnService.java
@@ -16,6 +16,8 @@
 
 package android.service.carrier;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
 import android.app.Service;
@@ -60,7 +62,8 @@
     };
 
     @Override
-    public IBinder onBind(Intent intent) {
+    @NonNull
+    public IBinder onBind(@Nullable Intent intent) {
         return mBinder;
     }
 
@@ -73,5 +76,6 @@
      * subId.
      */
     @WorkerThread
+    @NonNull
     public abstract List<ContentValues> onRestoreApns(int subId);
 }
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 74e6c6e..7ef40cd 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -17,6 +17,7 @@
 package android.service.notification;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.os.Parcel;
@@ -378,14 +379,14 @@
         /**
          * Builds the current ZenPolicy.
          */
-        public ZenPolicy build() {
+        public @NonNull ZenPolicy build() {
             return mZenPolicy.copy();
         }
 
         /**
          * Allows all notifications to bypass DND and unmutes all streams.
          */
-        public Builder allowAllSounds() {
+        public @NonNull Builder allowAllSounds() {
             for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
                 mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW);
             }
@@ -401,7 +402,7 @@
          * {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND,
          * the ringer stream is also muted.
          */
-        public Builder disallowAllSounds() {
+        public @NonNull Builder disallowAllSounds() {
             for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
                 mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW);
             }
@@ -413,7 +414,7 @@
         /**
          * Allows notifications intercepted by DND to show on all surfaces when DND is active.
          */
-        public Builder showAllVisualEffects() {
+        public @NonNull Builder showAllVisualEffects() {
             for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
                 mZenPolicy.mVisualEffects.set(i, STATE_ALLOW);
             }
@@ -423,7 +424,7 @@
         /**
          * Disallows notifications intercepted by DND from showing when DND is active.
          */
-        public Builder hideAllVisualEffects() {
+        public @NonNull Builder hideAllVisualEffects() {
             for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
                 mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW);
             }
@@ -435,7 +436,7 @@
          * unset categories will default to the current applied policy.
          * @hide
          */
-        public Builder unsetPriorityCategory(@PriorityCategory int category) {
+        public @NonNull Builder unsetPriorityCategory(@PriorityCategory int category) {
             mZenPolicy.mPriorityCategories.set(category, STATE_UNSET);
 
             if (category == PRIORITY_CATEGORY_MESSAGES) {
@@ -452,7 +453,7 @@
          * unset effects will default to the current applied policy.
          * @hide
          */
-        public Builder unsetVisualEffect(@VisualEffect int effect) {
+        public @NonNull Builder unsetVisualEffect(@VisualEffect int effect) {
             mZenPolicy.mVisualEffects.set(effect, STATE_UNSET);
             return this;
         }
@@ -461,7 +462,7 @@
          * Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER}
          * to play sounds and visually appear or to intercept them when DND is active.
          */
-        public Builder allowReminders(boolean allow) {
+        public @NonNull Builder allowReminders(boolean allow) {
             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS,
                     allow ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -471,7 +472,7 @@
          * Whether to allow notifications with category {@link Notification#CATEGORY_EVENT}
          * to play sounds and visually appear or to intercept them when DND is active.
          */
-        public Builder allowEvents(boolean allow) {
+        public @NonNull Builder allowEvents(boolean allow) {
             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS,
                     allow ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -483,7 +484,7 @@
          * them when DND is active.
          * @param audienceType message senders that are allowed to bypass DND
          */
-        public Builder allowMessages(@PeopleType int audienceType) {
+        public @NonNull Builder allowMessages(@PeopleType int audienceType) {
             if (audienceType == STATE_UNSET) {
                 return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES);
             }
@@ -507,7 +508,7 @@
          * them when DND is active.
          * @param audienceType callers that are allowed to bypass DND
          */
-        public Builder allowCalls(@PeopleType int audienceType) {
+        public @NonNull  Builder allowCalls(@PeopleType int audienceType) {
             if (audienceType == STATE_UNSET) {
                 return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS);
             }
@@ -530,7 +531,7 @@
          * {@link Notification#CATEGORY_CALL} that have recently called
          * to play sounds and visually appear.
          */
-        public Builder allowRepeatCallers(boolean allow) {
+        public @NonNull Builder allowRepeatCallers(boolean allow) {
             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS,
                     allow ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -542,7 +543,7 @@
          * to play sounds and visually appear or to intercept them when DND is active.
          * Disallowing alarms will mute the alarm stream when DND is active.
          */
-        public Builder allowAlarms(boolean allow) {
+        public @NonNull Builder allowAlarms(boolean allow) {
             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS,
                     allow ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -553,7 +554,7 @@
          * appear or to intercept them when DND is active.
          * Disallowing media will mute the media stream when DND is active.
          */
-        public Builder allowMedia(boolean allow) {
+        public @NonNull Builder allowMedia(boolean allow) {
             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA,
                     allow ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -563,7 +564,7 @@
          * Whether to allow system sounds to play when DND is active.
          * Disallowing system sounds will mute the system stream when DND is active.
          */
-        public Builder allowSystem(boolean allow) {
+        public @NonNull Builder allowSystem(boolean allow) {
             mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM,
                     allow ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -573,7 +574,7 @@
          * Whether to allow {@link PriorityCategory} sounds to play when DND is active.
          * @hide
          */
-        public Builder allowCategory(@PriorityCategory int category, boolean allow) {
+        public @NonNull Builder allowCategory(@PriorityCategory int category, boolean allow) {
             switch (category) {
                 case PRIORITY_CATEGORY_ALARMS:
                     allowAlarms(allow);
@@ -601,7 +602,7 @@
          * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
          * by DND are shown.
          */
-        public Builder showFullScreenIntent(boolean show) {
+        public @NonNull Builder showFullScreenIntent(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -611,7 +612,7 @@
          * Whether {@link NotificationChannel#shouldShowLights() notification lights} from
          * notifications intercepted by DND are blocked.
          */
-        public Builder showLights(boolean show) {
+        public @NonNull Builder showLights(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -620,7 +621,7 @@
         /**
          * Whether notifications intercepted by DND are prevented from peeking.
          */
-        public Builder showPeeking(boolean show) {
+        public @NonNull Builder showPeeking(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -630,7 +631,7 @@
          * Whether notifications intercepted by DND are prevented from appearing in the status bar
          * on devices that support status bars.
          */
-        public Builder showStatusBarIcons(boolean show) {
+        public @NonNull Builder showStatusBarIcons(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -640,7 +641,7 @@
          * Whether {@link NotificationChannel#canShowBadge() badges} from
          * notifications intercepted by DND are allowed on devices that support badging.
          */
-        public Builder showBadges(boolean show) {
+        public @NonNull Builder showBadges(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -650,7 +651,7 @@
          * Whether notification intercepted by DND are prevented from appearing on ambient displays
          * on devices that support ambient display.
          */
-        public Builder showInAmbientDisplay(boolean show) {
+        public @NonNull Builder showInAmbientDisplay(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -661,7 +662,7 @@
          * list views like the notification shade or lockscreen on devices that support those
          * views.
          */
-        public Builder showInNotificationList(boolean show) {
+        public @NonNull Builder showInNotificationList(boolean show) {
             mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST,
                     show ? STATE_ALLOW : STATE_DISALLOW);
             return this;
@@ -672,7 +673,7 @@
          * {@link VisualEffect}
          * @hide
          */
-        public Builder showVisualEffect(@VisualEffect int effect, boolean show) {
+        public @NonNull Builder showVisualEffect(@VisualEffect int effect, boolean show) {
             switch (effect) {
                 case VISUAL_EFFECT_FULL_SCREEN_INTENT:
                     showFullScreenIntent(show);
@@ -1001,7 +1002,7 @@
      * Makes deep copy of this ZenPolicy.
      * @hide
      */
-    public ZenPolicy copy() {
+    public @NonNull ZenPolicy copy() {
         final Parcel parcel = Parcel.obtain();
         try {
             writeToParcel(parcel, 0);
diff --git a/core/java/android/service/textclassifier/IConversationActionsCallback.aidl b/core/java/android/service/textclassifier/IConversationActionsCallback.aidl
deleted file mode 100644
index c35d424..0000000
--- a/core/java/android/service/textclassifier/IConversationActionsCallback.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.textclassifier;
-
-import android.view.textclassifier.ConversationActions;
-
-/**
- * Callback for a ConversationActions request.
- * @hide
- */
-oneway interface IConversationActionsCallback {
-    void onSuccess(in ConversationActions conversationActions);
-    void onFailure();
-}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/ITextClassificationCallback.aidl b/core/java/android/service/textclassifier/ITextClassificationCallback.aidl
deleted file mode 100644
index 10bfe63..0000000
--- a/core/java/android/service/textclassifier/ITextClassificationCallback.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.textclassifier;
-
-import android.view.textclassifier.TextClassification;
-
-/**
- * Callback for a TextClassification request.
- * @hide
- */
-oneway interface ITextClassificationCallback {
-    void onSuccess(in TextClassification classification);
-    void onFailure();
-}
diff --git a/core/java/android/service/textclassifier/ITextSelectionCallback.aidl b/core/java/android/service/textclassifier/ITextClassifierCallback.aidl
similarity index 82%
rename from core/java/android/service/textclassifier/ITextSelectionCallback.aidl
rename to core/java/android/service/textclassifier/ITextClassifierCallback.aidl
index 1b4c4d1..2926734 100644
--- a/core/java/android/service/textclassifier/ITextSelectionCallback.aidl
+++ b/core/java/android/service/textclassifier/ITextClassifierCallback.aidl
@@ -16,13 +16,14 @@
 
 package android.service.textclassifier;
 
+import android.os.Bundle;
 import android.view.textclassifier.TextSelection;
 
 /**
- * Callback for a TextSelection request.
+ * Callback for all requests from SystemTextClassifier.
  * @hide
  */
-oneway interface ITextSelectionCallback {
-    void onSuccess(in TextSelection selection);
+oneway interface ITextClassifierCallback {
+    void onSuccess(in Bundle result);
     void onFailure();
 }
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/ITextClassifierService.aidl b/core/java/android/service/textclassifier/ITextClassifierService.aidl
index 7941794..2f8d67b 100644
--- a/core/java/android/service/textclassifier/ITextClassifierService.aidl
+++ b/core/java/android/service/textclassifier/ITextClassifierService.aidl
@@ -16,11 +16,7 @@
 
 package android.service.textclassifier;
 
-import android.service.textclassifier.IConversationActionsCallback;
-import android.service.textclassifier.ITextClassificationCallback;
-import android.service.textclassifier.ITextLanguageCallback;
-import android.service.textclassifier.ITextLinksCallback;
-import android.service.textclassifier.ITextSelectionCallback;
+import android.service.textclassifier.ITextClassifierCallback;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
@@ -41,17 +37,17 @@
     void onSuggestSelection(
             in TextClassificationSessionId sessionId,
             in TextSelection.Request request,
-            in ITextSelectionCallback callback);
+            in ITextClassifierCallback callback);
 
     void onClassifyText(
             in TextClassificationSessionId sessionId,
             in TextClassification.Request request,
-            in ITextClassificationCallback callback);
+            in ITextClassifierCallback callback);
 
     void onGenerateLinks(
             in TextClassificationSessionId sessionId,
             in TextLinks.Request request,
-            in ITextLinksCallback callback);
+            in ITextClassifierCallback callback);
 
     // TODO: Remove
     void onSelectionEvent(
@@ -72,10 +68,10 @@
     void onDetectLanguage(
             in TextClassificationSessionId sessionId,
             in TextLanguage.Request request,
-            in ITextLanguageCallback callback);
+            in ITextClassifierCallback callback);
 
     void onSuggestConversationActions(
             in TextClassificationSessionId sessionId,
             in ConversationActions.Request request,
-            in IConversationActionsCallback callback);
+            in ITextClassifierCallback callback);
 }
diff --git a/core/java/android/service/textclassifier/ITextLanguageCallback.aidl b/core/java/android/service/textclassifier/ITextLanguageCallback.aidl
deleted file mode 100644
index 263d99af..0000000
--- a/core/java/android/service/textclassifier/ITextLanguageCallback.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.textclassifier;
-
-import android.view.textclassifier.TextLanguage;
-
-/**
- * Callback for a TextLanguage request.
- * @hide
- */
-oneway interface ITextLanguageCallback {
-    void onSuccess(in TextLanguage textLanguage);
-    void onFailure();
-}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 235b8e8..4088ce8 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -17,6 +17,7 @@
 package android.service.textclassifier;
 
 import android.Manifest;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -27,8 +28,12 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -46,6 +51,10 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.ref.WeakReference;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 /**
  * Abstract base class for the TextClassifier service.
  *
@@ -68,6 +77,11 @@
  *     </intent-filter>
  * </service>}</pre>
  *
+ * <p>From {@link android.os.Build.VERSION_CODES#Q} onward, all callbacks are called on the main
+ * thread. Prior to Q, there is no guarantee on what thread the callback will happen. You should
+ * make sure the callbacks are executed in your desired thread by using a executor, a handler or
+ * something else along the line.
+ *
  * @see TextClassifier
  * @hide
  */
@@ -85,201 +99,102 @@
     public static final String SERVICE_INTERFACE =
             "android.service.textclassifier.TextClassifierService";
 
+    /** @hide **/
+    private static final String KEY_RESULT = "key_result";
+
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), null, true);
+    private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
+
     private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
 
         // TODO(b/72533911): Implement cancellation signal
         @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
 
-        /** {@inheritDoc} */
         @Override
         public void onSuggestSelection(
                 TextClassificationSessionId sessionId,
-                TextSelection.Request request, ITextSelectionCallback callback) {
+                TextSelection.Request request, ITextClassifierCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
-            TextClassifierService.this.onSuggestSelection(
-                    sessionId, request, mCancellationSignal,
-                    new Callback<TextSelection>() {
-                        @Override
-                        public void onSuccess(TextSelection result) {
-                            try {
-                                callback.onSuccess(result);
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
+            mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
+                    sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
 
-                        @Override
-                        public void onFailure(CharSequence error) {
-                            try {
-                                if (callback.asBinder().isBinderAlive()) {
-                                    callback.onFailure();
-                                }
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-                    });
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onClassifyText(
                 TextClassificationSessionId sessionId,
-                TextClassification.Request request, ITextClassificationCallback callback) {
+                TextClassification.Request request, ITextClassifierCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
-            TextClassifierService.this.onClassifyText(
-                    sessionId, request, mCancellationSignal,
-                    new Callback<TextClassification>() {
-                        @Override
-                        public void onSuccess(TextClassification result) {
-                            try {
-                                callback.onSuccess(result);
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-
-                        @Override
-                        public void onFailure(CharSequence error) {
-                            try {
-                                callback.onFailure();
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-                    });
+            mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
+                    sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onGenerateLinks(
                 TextClassificationSessionId sessionId,
-                TextLinks.Request request, ITextLinksCallback callback) {
+                TextLinks.Request request, ITextClassifierCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
-            TextClassifierService.this.onGenerateLinks(
-                    sessionId, request,
-                    mCancellationSignal,
-                    new Callback<TextLinks>() {
-                        @Override
-                        public void onSuccess(TextLinks result) {
-                            try {
-                                callback.onSuccess(result);
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-
-                        @Override
-                        public void onFailure(CharSequence error) {
-                            try {
-                                callback.onFailure();
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-                    });
+            mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
+                    sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onSelectionEvent(
                 TextClassificationSessionId sessionId,
                 SelectionEvent event) {
             Preconditions.checkNotNull(event);
-            TextClassifierService.this.onSelectionEvent(sessionId, event);
+            mMainThreadHandler.post(
+                    () -> TextClassifierService.this.onSelectionEvent(sessionId, event));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onTextClassifierEvent(
                 TextClassificationSessionId sessionId,
                 TextClassifierEvent event) {
             Preconditions.checkNotNull(event);
-            TextClassifierService.this.onTextClassifierEvent(sessionId, event);
+            mMainThreadHandler.post(
+                    () -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onDetectLanguage(
                 TextClassificationSessionId sessionId,
                 TextLanguage.Request request,
-                ITextLanguageCallback callback) {
+                ITextClassifierCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
-            TextClassifierService.this.onDetectLanguage(
-                    sessionId,
-                    request,
-                    mCancellationSignal,
-                    new Callback<TextLanguage>() {
-                        @Override
-                        public void onSuccess(TextLanguage result) {
-                            try {
-                                callback.onSuccess(result);
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-
-                        @Override
-                        public void onFailure(CharSequence error) {
-                            try {
-                                callback.onFailure();
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        };
-                    });
+            mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
+                    sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onSuggestConversationActions(
                 TextClassificationSessionId sessionId,
                 ConversationActions.Request request,
-                IConversationActionsCallback callback) {
+                ITextClassifierCallback callback) {
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
-            TextClassifierService.this.onSuggestConversationActions(
-                    sessionId,
-                    request,
-                    mCancellationSignal,
-                    new Callback<ConversationActions>() {
-                        @Override
-                        public void onSuccess(ConversationActions result) {
-                            try {
-                                callback.onSuccess(result);
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-
-                        @Override
-                        public void onFailure(CharSequence error) {
-                            try {
-                                callback.onFailure();
-                            } catch (RemoteException e) {
-                                Slog.d(LOG_TAG, "Error calling callback");
-                            }
-                        }
-                    });
+            mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
+                    sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onCreateTextClassificationSession(
                 TextClassificationContext context, TextClassificationSessionId sessionId) {
             Preconditions.checkNotNull(context);
             Preconditions.checkNotNull(sessionId);
-            TextClassifierService.this.onCreateTextClassificationSession(context, sessionId);
+            mMainThreadHandler.post(
+                    () -> TextClassifierService.this.onCreateTextClassificationSession(
+                            context, sessionId));
         }
 
-        /** {@inheritDoc} */
         @Override
         public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId) {
-            TextClassifierService.this.onDestroyTextClassificationSession(sessionId);
+            mMainThreadHandler.post(
+                    () -> TextClassifierService.this.onDestroyTextClassificationSession(sessionId));
         }
     };
 
@@ -301,6 +216,7 @@
      * @param cancellationSignal object to watch for canceling the current operation
      * @param callback the callback to return the result to
      */
+    @MainThread
     public abstract void onSuggestSelection(
             @Nullable TextClassificationSessionId sessionId,
             @NonNull TextSelection.Request request,
@@ -316,6 +232,7 @@
      * @param cancellationSignal object to watch for canceling the current operation
      * @param callback the callback to return the result to
      */
+    @MainThread
     public abstract void onClassifyText(
             @Nullable TextClassificationSessionId sessionId,
             @NonNull TextClassification.Request request,
@@ -331,6 +248,7 @@
      * @param cancellationSignal object to watch for canceling the current operation
      * @param callback the callback to return the result to
      */
+    @MainThread
     public abstract void onGenerateLinks(
             @Nullable TextClassificationSessionId sessionId,
             @NonNull TextLinks.Request request,
@@ -345,12 +263,14 @@
      * @param cancellationSignal object to watch for canceling the current operation
      * @param callback the callback to return the result to
      */
+    @MainThread
     public void onDetectLanguage(
             @Nullable TextClassificationSessionId sessionId,
             @NonNull TextLanguage.Request request,
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextLanguage> callback) {
-        callback.onSuccess(getLocalTextClassifier().detectLanguage(request));
+        mSingleThreadExecutor.submit(() ->
+                callback.onSuccess(getLocalTextClassifier().detectLanguage(request)));
     }
 
     /**
@@ -361,12 +281,14 @@
      * @param cancellationSignal object to watch for canceling the current operation
      * @param callback the callback to return the result to
      */
+    @MainThread
     public void onSuggestConversationActions(
             @Nullable TextClassificationSessionId sessionId,
             @NonNull ConversationActions.Request request,
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<ConversationActions> callback) {
-        callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request));
+        mSingleThreadExecutor.submit(() ->
+                callback.onSuccess(getLocalTextClassifier().suggestConversationActions(request)));
     }
 
     /**
@@ -383,6 +305,7 @@
      *      instead
      */
     @Deprecated
+    @MainThread
     public void onSelectionEvent(
             @Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
 
@@ -396,6 +319,7 @@
      * @param sessionId the session id
      * @param event the TextClassifier event
      */
+    @MainThread
     public void onTextClassifierEvent(
             @Nullable TextClassificationSessionId sessionId, @NonNull TextClassifierEvent event) {}
 
@@ -405,6 +329,7 @@
      * @param context the text classification context
      * @param sessionId the session's Id
      */
+    @MainThread
     public void onCreateTextClassificationSession(
             @NonNull TextClassificationContext context,
             @NonNull TextClassificationSessionId sessionId) {}
@@ -414,6 +339,7 @@
      *
      * @param sessionId the id of the session to destroy
      */
+    @MainThread
     public void onDestroyTextClassificationSession(
             @NonNull TextClassificationSessionId sessionId) {}
 
@@ -441,6 +367,11 @@
         return TextClassifier.NO_OP;
     }
 
+    /** @hide **/
+    public static <T extends Parcelable> T getResponse(Bundle bundle) {
+        return bundle.getParcelable(KEY_RESULT);
+    }
+
     /**
      * Callbacks for TextClassifierService results.
      *
@@ -494,4 +425,44 @@
                 si.permission));
         return null;
     }
+
+    /**
+     * Forwards the callback result to a wrapped binder callback.
+     */
+    private static final class ProxyCallback<T extends Parcelable> implements Callback<T> {
+        private WeakReference<ITextClassifierCallback> mTextClassifierCallback;
+
+        private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
+            mTextClassifierCallback =
+                    new WeakReference<>(Preconditions.checkNotNull(textClassifierCallback));
+        }
+
+        @Override
+        public void onSuccess(T result) {
+            ITextClassifierCallback callback = mTextClassifierCallback.get();
+            if (callback == null) {
+                return;
+            }
+            try {
+                Bundle bundle = new Bundle(1);
+                bundle.putParcelable(KEY_RESULT, result);
+                callback.onSuccess(bundle);
+            } catch (RemoteException e) {
+                Slog.d(LOG_TAG, "Error calling callback");
+            }
+        }
+
+        @Override
+        public void onFailure(CharSequence error) {
+            ITextClassifierCallback callback = mTextClassifierCallback.get();
+            if (callback == null) {
+                return;
+            }
+            try {
+                callback.onFailure();
+            } catch (RemoteException e) {
+                Slog.d(LOG_TAG, "Error calling callback");
+            }
+        }
+    }
 }
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index a5d5af2..7fb0f95 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -88,7 +88,7 @@
         /**
          * Constructor called from {@link TextUtils} to restore the span from a parcel
          */
-        public Standard(Parcel src) {
+        public Standard(@NonNull Parcel src) {
             mHeight = src.readInt();
         }
 
diff --git a/core/java/android/util/DocumentsStatsLog.java b/core/java/android/util/DocumentsStatsLog.java
index f483944..a67bbde 100644
--- a/core/java/android/util/DocumentsStatsLog.java
+++ b/core/java/android/util/DocumentsStatsLog.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsProvider;
@@ -126,7 +127,7 @@
      *
      * @param packageName
      */
-    public static void logPickerLaunchedFrom(String packageName) {
+    public static void logPickerLaunchedFrom(@Nullable String packageName) {
         StatsLog.write(StatsLog.DOCS_UI_PICKER_LAUNCHED_FROM_REPORTED, packageName);
     }
 
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index 9ba0f5d..cf49803 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -21,6 +21,9 @@
 
 import libcore.util.EmptyArray;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * SparseArray mapping longs to Objects.  Unlike a normal array of Objects,
  * there can be gaps in the indices.  It is intended to be more memory efficient
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index af163ac..8dcdb40 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -22,6 +22,8 @@
 import android.annotation.UnsupportedAppUsage;
 import libcore.util.EmptyArray;
 
+import java.util.Arrays;
+
 /**
  * Map of {@code long} to {@code long}. Unlike a normal array of longs, there
  * can be gaps in the indices. It is intended to be more memory efficient than using a
diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING
new file mode 100644
index 0000000..87d428a
--- /dev/null
+++ b/core/java/android/view/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsUiRenderingTestCases"
+    },
+    {
+      "name": "CtsAccelerationTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 877b304..874be81 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18589,7 +18589,7 @@
             @FlagMap(target = FADING_EDGE_VERTICAL, name = "vertical"),
             @FlagMap(target = FADING_EDGE_HORIZONTAL, name = "horizontal")
     })
-    int getFadingEdge() {
+    public int getFadingEdge() {
         return mViewFlags & FADING_EDGE_MASK;
     }
 
@@ -18600,7 +18600,7 @@
      * @hide
      */
     @InspectableProperty
-    int getFadingEdgeLength() {
+    public int getFadingEdgeLength() {
         if (mScrollCache != null && (mViewFlags & FADING_EDGE_MASK) != FADING_EDGE_NONE) {
             return mScrollCache.fadingEdgeLength;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 86f85bf..b9dc0dd 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -24,7 +24,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.LocusId;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -212,11 +211,11 @@
     }
 
     /**
-     * Helper that creates a {@link ContentCaptureContext} associated with the given {@code uri}.
+     * Helper that creates a {@link ContentCaptureContext} associated with the given {@code id}.
      */
     @NonNull
-    public static ContentCaptureContext forLocusId(@NonNull Uri uri) {
-        return new Builder(new LocusId(uri)).build();
+    public static ContentCaptureContext forLocusId(@NonNull String id) {
+        return new Builder(new LocusId(id)).build();
     }
 
     /**
diff --git a/core/java/android/view/contentcapture/UserDataRemovalRequest.java b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
index b273f7c..3e1e4ab 100644
--- a/core/java/android/view/contentcapture/UserDataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
@@ -15,6 +15,7 @@
  */
 package android.view.contentcapture;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.ActivityThread;
 import android.content.LocusId;
@@ -24,6 +25,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -33,6 +36,19 @@
  */
 public final class UserDataRemovalRequest implements Parcelable {
 
+    /**
+     * When set, service should use the {@link LocusId#getId()} as prefix for the data to be
+     * removed.
+     */
+    public static final int FLAG_IS_PREFIX = 0x1;
+
+    /** @hide */
+    @IntDef(prefix = { "FLAG" }, flag = true, value = {
+            FLAG_IS_PREFIX
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Flags {}
+
     private final String mPackageName;
 
     private final boolean mForEverything;
@@ -46,7 +62,7 @@
             mLocusIdRequests = new ArrayList<>(size);
             for (int i = 0; i < size; i++) {
                 mLocusIdRequests.add(new LocusIdRequest(builder.mLocusIds.get(i),
-                        builder.mRecursive.get(i) == 1));
+                        builder.mFlags.get(i)));
             }
         }
     }
@@ -59,7 +75,7 @@
             mLocusIdRequests = new ArrayList<>(size);
             for (int i = 0; i < size; i++) {
                 mLocusIdRequests.add(new LocusIdRequest((LocusId) parcel.readValue(null),
-                        parcel.readBoolean()));
+                        parcel.readInt()));
             }
         }
     }
@@ -94,7 +110,7 @@
 
         private boolean mForEverything;
         private ArrayList<LocusId> mLocusIds;
-        private IntArray mRecursive;
+        private IntArray mFlags;
 
         private boolean mDestroyed;
 
@@ -116,24 +132,24 @@
          * Request service to remove data associated with a given {@link LocusId}.
          *
          * @param locusId the {@link LocusId} being requested to be removed.
-         * @param recursive whether it should remove the data associated with just the
-         * {@code LocusId} or its tree of descendants.
+         * @param flags either {@link UserDataRemovalRequest#FLAG_IS_PREFIX} or {@code 0}
          *
          * @return this builder
          */
         @NonNull
-        public Builder addLocusId(@NonNull LocusId locusId, boolean recursive) {
+        public Builder addLocusId(@NonNull LocusId locusId, @Flags int flags) {
             throwIfDestroyed();
             Preconditions.checkState(!mForEverything, "Already is for everything");
             Preconditions.checkNotNull(locusId);
+            // felipeal: check flags
 
             if (mLocusIds == null) {
                 mLocusIds = new ArrayList<>();
-                mRecursive = new IntArray();
+                mFlags = new IntArray();
             }
 
             mLocusIds.add(locusId);
-            mRecursive.add(recursive ? 1 : 0);
+            mFlags.add(flags);
             return this;
         }
 
@@ -144,7 +160,8 @@
         public UserDataRemovalRequest build() {
             throwIfDestroyed();
 
-            Preconditions.checkState(mForEverything || mLocusIds != null);
+            Preconditions.checkState(mForEverything || mLocusIds != null,
+                    "must call either #forEverything() or add one #addLocusId()");
 
             mDestroyed = true;
             return new UserDataRemovalRequest(this);
@@ -170,7 +187,7 @@
             for (int i = 0; i < size; i++) {
                 final LocusIdRequest request = mLocusIdRequests.get(i);
                 parcel.writeValue(request.getLocusId());
-                parcel.writeBoolean(request.isRecursive());
+                parcel.writeInt(request.getFlags());
             }
         }
     }
@@ -196,11 +213,11 @@
      */
     public final class LocusIdRequest {
         private final @NonNull LocusId mLocusId;
-        private final boolean mRecursive;
+        private final @Flags int mFlags;
 
-        private LocusIdRequest(@NonNull LocusId locusId, boolean recursive) {
+        private LocusIdRequest(@NonNull LocusId locusId, @Flags int flags) {
             this.mLocusId = locusId;
-            this.mRecursive = recursive;
+            this.mFlags = flags;
         }
 
         /**
@@ -212,12 +229,13 @@
         }
 
         /**
-         * Checks whether the request is to remove just the data associated with the {@link LocusId}
-         *  per se, or also its descendants.
+         * Gets the flags associates with request.
+         *
+         * @return either {@link UserDataRemovalRequest#FLAG_IS_PREFIX} or {@code 0}.
          */
         @NonNull
-        public boolean isRecursive() {
-            return mRecursive;
+        public @Flags int getFlags() {
+            return mFlags;
         }
     }
 }
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index f859521..30938c1 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -16,6 +16,7 @@
 
 package android.view.inspector;
 
+import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -39,7 +40,7 @@
  * @see InspectionCompanion#readProperties(Object, PropertyReader)
  * @hide
  */
-@Target({METHOD})
+@Target({METHOD, FIELD})
 @Retention(SOURCE)
 @TestApi
 public @interface InspectableProperty {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 8b370f5..8f8766e 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -20,15 +20,14 @@
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Looper;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.service.textclassifier.IConversationActionsCallback;
-import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextClassifierCallback;
 import android.service.textclassifier.ITextClassifierService;
-import android.service.textclassifier.ITextLanguageCallback;
-import android.service.textclassifier.ITextLinksCallback;
-import android.service.textclassifier.ITextSelectionCallback;
+import android.service.textclassifier.TextClassifierService;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -73,9 +72,10 @@
         Utils.checkMainThread();
         try {
             request.setCallingPackageName(mPackageName);
-            final TextSelectionCallback callback = new TextSelectionCallback();
+            final BlockingCallback<TextSelection> callback =
+                    new BlockingCallback<>("textselection");
             mManagerService.onSuggestSelection(mSessionId, request, callback);
-            final TextSelection selection = callback.mReceiver.get();
+            final TextSelection selection = callback.get();
             if (selection != null) {
                 return selection;
             }
@@ -95,9 +95,10 @@
         Utils.checkMainThread();
         try {
             request.setCallingPackageName(mPackageName);
-            final TextClassificationCallback callback = new TextClassificationCallback();
+            final BlockingCallback<TextClassification> callback =
+                    new BlockingCallback<>("textclassification");
             mManagerService.onClassifyText(mSessionId, request, callback);
-            final TextClassification classification = callback.mReceiver.get();
+            final TextClassification classification = callback.get();
             if (classification != null) {
                 return classification;
             }
@@ -122,9 +123,10 @@
 
         try {
             request.setCallingPackageName(mPackageName);
-            final TextLinksCallback callback = new TextLinksCallback();
+            final BlockingCallback<TextLinks> callback =
+                    new BlockingCallback<>("textlinks");
             mManagerService.onGenerateLinks(mSessionId, request, callback);
-            final TextLinks links = callback.mReceiver.get();
+            final TextLinks links = callback.get();
             if (links != null) {
                 return links;
             }
@@ -165,9 +167,10 @@
 
         try {
             request.setCallingPackageName(mPackageName);
-            final TextLanguageCallback callback = new TextLanguageCallback();
+            final BlockingCallback<TextLanguage> callback =
+                    new BlockingCallback<>("textlanguage");
             mManagerService.onDetectLanguage(mSessionId, request, callback);
-            final TextLanguage textLanguage = callback.mReceiver.get();
+            final TextLanguage textLanguage = callback.get();
             if (textLanguage != null) {
                 return textLanguage;
             }
@@ -184,9 +187,10 @@
 
         try {
             request.setCallingPackageName(mPackageName);
-            final ConversationActionsCallback callback = new ConversationActionsCallback();
+            final BlockingCallback<ConversationActions> callback =
+                    new BlockingCallback<>("conversation-actions");
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
-            final ConversationActions conversationActions = callback.mReceiver.get();
+            final ConversationActions conversationActions = callback.get();
             if (conversationActions != null) {
                 return conversationActions;
             }
@@ -245,82 +249,28 @@
         }
     }
 
-    private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
+    private static final class BlockingCallback<T extends Parcelable>
+            extends ITextClassifierCallback.Stub {
+        private final ResponseReceiver<T> mReceiver;
 
-        final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>("textselection");
+        BlockingCallback(String name) {
+            mReceiver = new ResponseReceiver<>(name);
+        }
 
         @Override
-        public void onSuccess(TextSelection selection) {
-            mReceiver.onSuccess(selection);
+        public void onSuccess(Bundle result) {
+            mReceiver.onSuccess(TextClassifierService.getResponse(result));
         }
 
         @Override
         public void onFailure() {
             mReceiver.onFailure();
         }
-    }
 
-    private static final class TextClassificationCallback extends ITextClassificationCallback.Stub {
-
-        final ResponseReceiver<TextClassification> mReceiver =
-                new ResponseReceiver<>("textclassification");
-
-        @Override
-        public void onSuccess(TextClassification classification) {
-            mReceiver.onSuccess(classification);
+        public T get() {
+            return mReceiver.get();
         }
 
-        @Override
-        public void onFailure() {
-            mReceiver.onFailure();
-        }
-    }
-
-    private static final class TextLinksCallback extends ITextLinksCallback.Stub {
-
-        final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>("textlinks");
-
-        @Override
-        public void onSuccess(TextLinks links) {
-            mReceiver.onSuccess(links);
-        }
-
-        @Override
-        public void onFailure() {
-            mReceiver.onFailure();
-        }
-    }
-
-    private static final class TextLanguageCallback extends ITextLanguageCallback.Stub {
-
-        final ResponseReceiver<TextLanguage> mReceiver = new ResponseReceiver<>("textlanguage");
-
-        @Override
-        public void onSuccess(TextLanguage textLanguage) {
-            mReceiver.onSuccess(textLanguage);
-        }
-
-        @Override
-        public void onFailure() {
-            mReceiver.onFailure();
-        }
-    }
-
-    private static final class ConversationActionsCallback
-            extends IConversationActionsCallback.Stub {
-
-        final ResponseReceiver<ConversationActions> mReceiver =
-                new ResponseReceiver<>("conversationaction");
-
-        @Override
-        public void onSuccess(ConversationActions conversationActions) {
-            mReceiver.onSuccess(conversationActions);
-        }
-
-        @Override
-        public void onFailure() {
-            mReceiver.onFailure();
-        }
     }
 
     private static final class ResponseReceiver<T> {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 73792b0..04bcb14 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12744,7 +12744,7 @@
      * return value may not be the same as the one TextView uses if the View's layout direction is
      * not resolved or detached from parent root view.
      */
-    public TextDirectionHeuristic getTextDirectionHeuristic() {
+    public @NonNull TextDirectionHeuristic getTextDirectionHeuristic() {
         if (hasPasswordTransformationMethod()) {
             // passwords fields should be LTR
             return TextDirectionHeuristics.LTR;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a5a1dd9..67e0446 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1889,11 +1889,19 @@
          * Set to true to reveal all service targets at once.
          */
         public void setShowServiceTargets(boolean show) {
+            // mShowServiceTargets is only flipped once to show direct share targets. But after the
+            // initial display the list can be re-sorted and the user will see the target list
+            // change. This will log the initial show and the subsequent shuffle to help us get
+            // accurate timing of the UX.
+            if (show) {
+                getMetricsLogger().write(
+                        new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET)
+                                .setSubtype(mShowServiceTargets ? MetricsEvent.PREVIOUSLY_VISIBLE
+                                        : MetricsEvent.PREVIOUSLY_HIDDEN));
+            }
             if (show != mShowServiceTargets) {
                 mShowServiceTargets = show;
                 notifyDataSetChanged();
-                getMetricsLogger().write(
-                        new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN_DIRECT_TARGET));
             }
         }
 
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index c096961..c4af4c7 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -48,9 +48,9 @@
     @UnsupportedAppUsage
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
     void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis,
-            long endTimeMillis, in RemoteCallback callback);
+            long endTimeMillis, int flags, in RemoteCallback callback);
     void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops,
-            long beginTimeMillis, long endTimeMillis, in RemoteCallback callback);
+            long beginTimeMillis, long endTimeMillis, int flags, in RemoteCallback callback);
     void offsetHistory(long duration);
     void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
     void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index f3bc2c7..445075d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -616,7 +616,7 @@
     // OS: Q
     ACTION_PANEL_INTERACTION = 1658;
 
-    // ACTION: Show Contextual homepage, log latency in loading cards
+    // ACTION: Show Contextual homepage. Log total loading latency.
     ACTION_CONTEXTUAL_HOME_SHOW = 1662;
 
     // ACTION: Contextual card displays
@@ -649,6 +649,15 @@
     ACTION_ATCSCUC = 1680;
 
     ACTION_ATCHNUC = 1681;
+
+    // ACTION: Individual contextual card loading time
+    ACTION_CONTEXTUAL_CARD_LOAD = 1684;
+
+    //ACTION: Contextual card loading timeout
+    ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT = 1685;
+
+    //ACTION: Log result for each card's eligibility check
+    ACTION_CONTEXTUAL_CARD_ELIGIBILITY = 1686;
 }
 
 /**
@@ -2287,4 +2296,7 @@
 
     // OPEN: Accessibility detail settings (android.settings.ACCESSIBILITY_DETAILS_SETTINGS intent)
     ACCESSIBILITY_DETAILS_SETTINGS = 1682;
+
+    // Open: Settings will show the conditional when Grayscale mode is on
+    SETTINGS_CONDITION_GRAYSCALE_MODE = 1683;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e87295a..ae3e714 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4445,15 +4445,23 @@
          @hide -->
     <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
         android:protectionLevel="signature|preinstalled" />
+
     <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
          @hide -->
     <permission android:name="android.permission.AMBIENT_WALLPAPER"
                 android:protectionLevel="signature|preinstalled" />
+
     <!-- @SystemApi Allows sensor privacy to be modified.
          @hide -->
     <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an activity to replace the app name and icon displayed in share targets
+         in the sharesheet for the Q-release and later.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
index b08b0f4..9fd615d 100644
--- a/core/res/res/layout/immersive_mode_cling.xml
+++ b/core/res/res/layout/immersive_mode_cling.xml
@@ -59,7 +59,7 @@
             android:paddingStart="48dp"
             android:paddingTop="40dp"
             android:text="@string/immersive_cling_title"
-            android:textColor="@color/primary_text_default_material_light"
+            android:textColor="@android:color/white"
             android:textSize="24sp" />
 
     <TextView
@@ -71,7 +71,7 @@
             android:paddingStart="48dp"
             android:paddingTop="12.6dp"
             android:text="@string/immersive_cling_description"
-            android:textColor="@color/primary_text_default_material_light"
+            android:textColor="@android:color/white"
             android:textSize="16sp" />
 
     <Button
@@ -89,4 +89,4 @@
             android:textColor="@android:color/white"
             android:textSize="14sp" />
 
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 47e5462..2f2fdca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3945,4 +3945,7 @@
      and a second time clipped to the fill level to indicate charge -->
     <bool name="config_batterymeterDualTone">false</bool>
 
+    <!-- The default peak refresh rate for a given device. Change this value if you want to allow
+         for higher refresh rates to be automatically used out of the box -->
+    <integer name="config_defaultPeakRefreshRate">60</integer>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 426d813..d5444c0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3688,4 +3688,6 @@
   <java-symbol type="string" name="mime_type_spreadsheet_ext" />
   <java-symbol type="string" name="mime_type_presentation" />
   <java-symbol type="string" name="mime_type_presentation_ext" />
+
+  <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
 </resources>
diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk
index 6b0484e..faffc4b 100644
--- a/core/tests/BroadcastRadioTests/Android.mk
+++ b/core/tests/BroadcastRadioTests/Android.mk
@@ -25,7 +25,7 @@
 # LOCAL_SDK_VERSION := current
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util androidx.test.rules testng
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt androidx.test.rules testng
 
 LOCAL_JAVA_LIBRARIES := android.test.base
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 69632c1..ebc6be7 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -92,7 +92,8 @@
                     Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
                     Settings.System.VOLUME_VOICE, // deprecated since API 2?
                     Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
-                    Settings.System.WINDOW_ORIENTATION_LISTENER_LOG // used for debugging only
+                    Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+                    Settings.System.PEAK_REFRESH_RATE // depends on hardware capabilities
                     );
 
     private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index a5ac270..de2edc3 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -25,7 +25,6 @@
 import static org.testng.Assert.assertThrows;
 
 import android.content.LocusId;
-import android.net.Uri;
 import android.os.Parcel;
 import android.os.SystemClock;
 import android.view.autofill.AutofillId;
@@ -47,7 +46,7 @@
 
     private static final long MY_EPOCH = SystemClock.uptimeMillis();
 
-    private static final LocusId ID = new LocusId(Uri.parse("WHATEVER"));
+    private static final LocusId ID = new LocusId("WHATEVER");
 
     // Not using @Mock because it's final - no need to be fancy here....
     private final ContentCaptureContext mClientContext =
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index 2110a8f..3e53a38 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -33,6 +33,7 @@
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
         <permission name="android.permission.MASTER_CLEAR"/>
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.MOVE_PACKAGE"/>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 7016cc7..2cf802b 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -59,6 +59,8 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.Objects;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -838,6 +840,40 @@
     }
 
     /**
+     * Return if the given MIME type is a supported file format that can be
+     * decoded by this class. This can be useful to determine if a file can be
+     * decoded directly, or if it needs to be converted into a more general
+     * format using an API like {@link ContentResolver#openTypedAssetFile}.
+     */
+    public static boolean isMimeTypeSupported(@NonNull String mimeType) {
+        Objects.requireNonNull(mimeType);
+        switch (mimeType.toLowerCase(Locale.US)) {
+            case "image/png":
+            case "image/jpeg":
+            case "image/webp":
+            case "image/gif":
+            case "image/heif":
+            case "image/heic":
+            case "image/bmp":
+            case "image/x-ico":
+            case "image/vnd.wap.wbmp":
+            case "image/x-sony-arw":
+            case "image/x-canon-cr2":
+            case "image/x-adobe-dng":
+            case "image/x-nikon-nef":
+            case "image/x-nikon-nrw":
+            case "image/x-olympus-orf":
+            case "image/x-fuji-raf":
+            case "image/x-panasonic-rw2":
+            case "image/x-pentax-pef":
+            case "image/x-samsung-srw":
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
      * Create a new {@link Source Source} from a resource.
      *
      * @param res the {@link Resources} object containing the image data.
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 42b6acd..0244942 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -62,7 +62,7 @@
  *
  * <h3>Creating a RenderNode</h3>
  * <pre class="prettyprint">
- *     RenderNode renderNode = RenderNode.create("myRenderNode");
+ *     RenderNode renderNode = new RenderNode("myRenderNode");
  *     renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50
  *     RecordingCanvas canvas = renderNode.beginRecording();
  *     try {
@@ -106,7 +106,7 @@
  *
  * <pre class="prettyprint">
  *     private void createDisplayList() {
- *         mRenderNode = RenderNode.create("MyRenderNode");
+ *         mRenderNode = new RenderNode("MyRenderNode");
  *         mRenderNode.setLeftTopRightBottom(0, 0, width, height);
  *         RecordingCanvas canvas = mRenderNode.beginRecording();
  *         try {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 6ecb621..1894e32 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -581,9 +581,9 @@
      * The default value for this property is {@code false}.
      * <p>
      * <strong>Note</strong>: This property corresponds to the
-     * {@code android:useLevel} attribute on the inner {@code &lt;gradient&gt;}
+     * {@code android:useLevel} attribute on the inner {@code <gradient>}
      * tag, NOT the {@code android:useLevel} attribute on the outer
-     * {@code &lt;shape&gt;} tag. For example,
+     * {@code <shape>} tag. For example,
      * <pre>{@code
      * <shape ...>
      *     <gradient
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
index 596e5f5..99d3197 100644
--- a/keystore/tests/Android.mk
+++ b/keystore/tests/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test
+    androidx.test.rules hamcrest-library
 
 LOCAL_PACKAGE_NAME := KeystoreTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
index 9bf2d0c..6833cd1 100644
--- a/keystore/tests/AndroidManifest.xml
+++ b/keystore/tests/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.security.tests"
         android:label="Tests for Keystore">
     </instrumentation>
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 32f8ec4..fca2775 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -22,16 +22,20 @@
 
 import android.os.Parcel;
 import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
-import android.support.test.runner.AndroidJUnit4;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.math.BigInteger;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.RSAKeyGenParameterSpec;
 import java.util.Date;
+
 import javax.security.auth.x500.X500Principal;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /** Unit tests for {@link ParcelableKeyGenParameterSpec}. */
 @RunWith(AndroidJUnit4.class)
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
index 865cad4..b2edfd0 100644
--- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -20,10 +20,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
+import android.security.ParcelableKeyGenParameterSpecTest;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
-import android.security.ParcelableKeyGenParameterSpecTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 117d05e..d74f27c 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1052,6 +1052,8 @@
 }
 
 void AssetManager2::InvalidateCaches(uint32_t diff) {
+  cached_bag_resid_stacks_.clear();
+
   if (diff == 0xffffffffu) {
     // Everything must go.
     cached_bags_.clear();
diff --git a/libs/hwui/TEST_MAPPING b/libs/hwui/TEST_MAPPING
new file mode 100644
index 0000000..d9f2acb
--- /dev/null
+++ b/libs/hwui/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsUiRenderingTestCases"
+    },
+    {
+      "name": "CtsGraphicsTestCases"
+    },
+    {
+      "name": "CtsAccelerationTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index 5e8eac1..f056d3b 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -47,12 +47,16 @@
     void setAll(bool all);
     void setDest(int dest);
     void addSection(int section);
+    void setReceiverPkg(const string& pkg);
+    void setReceiverCls(const string& cls);
     void addHeader(const vector<uint8_t>& headerProto);
 
     inline bool all() const { return mAll; }
     bool containsSection(int section) const;
     inline int dest() const { return mDest; }
     inline const set<int>& sections() const { return mSections; }
+    inline const String16& receiverPkg() const { return mReceiverPkg; }
+    inline const String16& receiverCls() const { return mReceiverCls; }
     inline const vector<vector<uint8_t>>& headers() const { return mHeaders; }
 
     void merge(const IncidentReportArgs& that);
@@ -62,6 +66,8 @@
     vector<vector<uint8_t>> mHeaders;
     bool mAll;
     int mDest;
+    String16 mReceiverPkg;
+    String16 mReceiverCls;
 };
 
 }
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index 06b7a5b..46c8dcf9 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -81,6 +81,16 @@
         return err;
     }
 
+    err = out->writeString16(mReceiverPkg);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = out->writeString16(mReceiverCls);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
     return NO_ERROR;
 }
 
@@ -134,6 +144,9 @@
     }
     mDest = dest;
 
+    mReceiverPkg = in->readString16();
+    mReceiverCls = in->readString16();
+
     return OK;
 }
 
@@ -161,6 +174,18 @@
 }
 
 void
+IncidentReportArgs::setReceiverPkg(const string& pkg)
+{
+    mReceiverPkg = String16(pkg.c_str());
+}
+
+void
+IncidentReportArgs::setReceiverCls(const string& cls)
+{
+    mReceiverCls = String16(cls.c_str());
+}
+
+void
 IncidentReportArgs::addHeader(const vector<uint8_t>& headerProto)
 {
     mHeaders.push_back(headerProto);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index ed74333..17e2509 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -113,10 +113,6 @@
      * Bit mask for mFieldsMask indicating the presence of mBearingAccuracy.
      */
     private static final int HAS_BEARING_ACCURACY_MASK = 128;
-    /**
-     * Bit mask for mFieldsMask indicating the presence of mElapsedRealtimeUncertaintyNanos.
-     */
-    private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK = 256;
 
     // Cached data to make bearing/distance computations more efficient for the case
     // where distanceTo and bearingTo are called in sequence.  Assume this typically happens
@@ -134,9 +130,6 @@
     private long mTime = 0;
     @UnsupportedAppUsage
     private long mElapsedRealtimeNanos = 0;
-    // Estimate of the relative precision of the alignment of this SystemClock
-    // timestamp, with the reported measurements in nanoseconds (68% confidence).
-    private long mElapsedRealtimeUncertaintyNanos = 0;
     private double mLatitude = 0.0;
     private double mLongitude = 0.0;
     private double mAltitude = 0.0f;
@@ -178,7 +171,6 @@
         mProvider = l.mProvider;
         mTime = l.mTime;
         mElapsedRealtimeNanos = l.mElapsedRealtimeNanos;
-        mElapsedRealtimeUncertaintyNanos = l.mElapsedRealtimeUncertaintyNanos;
         mFieldsMask = l.mFieldsMask;
         mLatitude = l.mLatitude;
         mLongitude = l.mLongitude;
@@ -199,7 +191,6 @@
         mProvider = null;
         mTime = 0;
         mElapsedRealtimeNanos = 0;
-        mElapsedRealtimeUncertaintyNanos = 0;
         mFieldsMask = 0;
         mLatitude = 0;
         mLongitude = 0;
@@ -595,37 +586,6 @@
     }
 
     /**
-     * Get estimate of the relative precision of the alignment of the
-     * ElapsedRealtimeNanos timestamp, with the reported measurements in
-     * nanoseconds (68% confidence).
-     *
-     * @return uncertainty of elapsed real-time of fix, in nanoseconds.
-     */
-    public long getElapsedRealtimeUncertaintyNanos() {
-        return mElapsedRealtimeUncertaintyNanos;
-    }
-
-    /**
-     * Set estimate of the relative precision of the alignment of the
-     * ElapsedRealtimeNanos timestamp, with the reported measurements in
-     * nanoseconds (68% confidence).
-     *
-     * @param time uncertainty of the elapsed real-time of fix, in nanoseconds.
-     */
-    public void setElapsedRealtimeUncertaintyNanos(long time) {
-        mElapsedRealtimeUncertaintyNanos = time;
-        mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
-    }
-
-    /**
-     * True if this location has a elapsed realtime accuracy.
-     */
-    public boolean hasElapsedRealtimeUncertaintyNanos() {
-        return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0;
-    }
-
-
-    /**
      * Get the latitude, in degrees.
      *
      * <p>All locations generated by the {@link LocationManager}
@@ -1102,10 +1062,6 @@
             s.append(" et=");
             TimeUtils.formatDuration(mElapsedRealtimeNanos / 1000000L, s);
         }
-        if (hasElapsedRealtimeUncertaintyNanos()) {
-            s.append(" etAcc=");
-            TimeUtils.formatDuration(mElapsedRealtimeUncertaintyNanos / 1000000L, s);
-        }
         if (hasAltitude()) s.append(" alt=").append(mAltitude);
         if (hasSpeed()) s.append(" vel=").append(mSpeed);
         if (hasBearing()) s.append(" bear=").append(mBearing);
@@ -1136,7 +1092,6 @@
             Location l = new Location(provider);
             l.mTime = in.readLong();
             l.mElapsedRealtimeNanos = in.readLong();
-            l.mElapsedRealtimeUncertaintyNanos = in.readLong();
             l.mFieldsMask = in.readByte();
             l.mLatitude = in.readDouble();
             l.mLongitude = in.readDouble();
@@ -1167,7 +1122,6 @@
         parcel.writeString(mProvider);
         parcel.writeLong(mTime);
         parcel.writeLong(mElapsedRealtimeNanos);
-        parcel.writeLong(mElapsedRealtimeUncertaintyNanos);
         parcel.writeByte(mFieldsMask);
         parcel.writeDouble(mLatitude);
         parcel.writeDouble(mLongitude);
diff --git a/media/apex/java/android/media/CallbackDataSourceDesc.java b/media/apex/java/android/media/CallbackDataSourceDesc.java
index cd36414..9209ca9 100644
--- a/media/apex/java/android/media/CallbackDataSourceDesc.java
+++ b/media/apex/java/android/media/CallbackDataSourceDesc.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 /**
  * Structure of data source descriptor for sources using callback.
@@ -37,7 +38,7 @@
      * Return the DataSourceCallback of this data source.
      * @return the DataSourceCallback of this data source
      */
-    public DataSourceCallback getDataSourceCallback() {
+    public @NonNull DataSourceCallback getDataSourceCallback() {
         return mDataSourceCallback;
     }
 
@@ -70,7 +71,7 @@
          * @param dsd the {@link CallbackDataSourceDesc} object whose data will be reused
          * in the new Builder.
          */
-        public Builder(CallbackDataSourceDesc dsd) {
+        public Builder(@Nullable CallbackDataSourceDesc dsd) {
             super(dsd);
             if (dsd == null) {
                 return;  // use default
@@ -86,6 +87,11 @@
          * @return a new {@link CallbackDataSourceDesc} object
          */
         public @NonNull CallbackDataSourceDesc build() {
+            if (mDataSourceCallback == null) {
+                throw new IllegalStateException(
+                        "DataSourceCallback should not be null");
+            }
+
             CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
             super.build(dsd);
             dsd.mDataSourceCallback = mDataSourceCallback;
diff --git a/media/apex/java/android/media/DataSourceCallback.java b/media/apex/java/android/media/DataSourceCallback.java
index 1afcd20..6515bd6 100644
--- a/media/apex/java/android/media/DataSourceCallback.java
+++ b/media/apex/java/android/media/DataSourceCallback.java
@@ -17,6 +17,8 @@
 
 package android.media;
 
+import android.annotation.NonNull;
+
 import java.io.Closeable;
 import java.io.IOException;
 
@@ -49,7 +51,7 @@
      * @throws IOException on fatal errors.
      * @return the number of bytes read, or -1 if end of stream is reached.
      */
-    public abstract int readAt(long position, byte[] buffer, int offset, int size)
+    public abstract int readAt(long position, @NonNull byte[] buffer, int offset, int size)
             throws IOException;
 
     /**
diff --git a/media/apex/java/android/media/DataSourceDesc.java b/media/apex/java/android/media/DataSourceDesc.java
index 7fc6f79..e6fd120 100644
--- a/media/apex/java/android/media/DataSourceDesc.java
+++ b/media/apex/java/android/media/DataSourceDesc.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 /**
  * Base class of data source descriptor.
@@ -64,7 +65,7 @@
      * Return the media Id of data source.
      * @return the media Id of data source
      */
-    public String getMediaId() {
+    public @Nullable String getMediaId() {
         return mMediaId;
     }
 
@@ -149,7 +150,7 @@
          * @param mediaId the media Id of this data source
          * @return the same Builder instance.
          */
-        public @NonNull T setMediaId(String mediaId) {
+        public @NonNull T setMediaId(@Nullable String mediaId) {
             mMediaId = mediaId;
             return (T) this;
         }
diff --git a/media/apex/java/android/media/FileDataSourceDesc.java b/media/apex/java/android/media/FileDataSourceDesc.java
index e29bd00..4b70367 100644
--- a/media/apex/java/android/media/FileDataSourceDesc.java
+++ b/media/apex/java/android/media/FileDataSourceDesc.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
@@ -106,7 +107,7 @@
      * Return the ParcelFileDescriptor of this data source.
      * @return the ParcelFileDescriptor of this data source
      */
-    public ParcelFileDescriptor getParcelFileDescriptor() {
+    public @NonNull ParcelFileDescriptor getParcelFileDescriptor() {
         return mPFD;
     }
 
@@ -159,7 +160,7 @@
          * @param dsd the {@link FileDataSourceDesc} object whose data will be reused
          * in the new Builder.
          */
-        public Builder(FileDataSourceDesc dsd) {
+        public Builder(@Nullable FileDataSourceDesc dsd) {
             super(dsd);
             if (dsd == null) {
                 return;  // use default
diff --git a/media/apex/java/android/media/MediaItem2.java b/media/apex/java/android/media/MediaItem2.java
index fc0f08e..ff0d43e 100644
--- a/media/apex/java/android/media/MediaItem2.java
+++ b/media/apex/java/android/media/MediaItem2.java
@@ -245,7 +245,7 @@
     /**
      * Builder for {@link MediaItem2}.
      */
-    public static class Builder {
+    public static final class Builder {
         @SuppressWarnings("WeakerAccess") /* synthetic access */
         MediaMetadata mMetadata;
         @SuppressWarnings("WeakerAccess") /* synthetic access */
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
index ef30172..f6b2031 100644
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ b/media/apex/java/android/media/MediaPlayer2.java
@@ -344,7 +344,7 @@
      * to free the resources. If not released, too many MediaPlayer2 instances may
      * result in an exception.</p>
      */
-    public MediaPlayer2(Context context) {
+    public MediaPlayer2(@NonNull Context context) {
         mGuard.open("close");
 
         mContext = context;
@@ -488,7 +488,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object play() {
+    public @NonNull Object play() {
         return addTask(new Task(CALL_COMPLETED_PLAY, false) {
             @Override
             void process() {
@@ -508,7 +508,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object prepare() {
+    public @NonNull Object prepare() {
         return addTask(new Task(CALL_COMPLETED_PREPARE, true) {
             @Override
             void process() {
@@ -524,7 +524,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object pause() {
+    public @NonNull Object pause() {
         return addTask(new Task(CALL_COMPLETED_PAUSE, false) {
             @Override
             void process() {
@@ -542,7 +542,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object skipToNext() {
+    public @NonNull Object skipToNext() {
         return addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
             @Override
             void process() {
@@ -700,7 +700,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setAudioAttributes(@NonNull AudioAttributes attributes) {
+    public @NonNull Object setAudioAttributes(@NonNull AudioAttributes attributes) {
         return addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
             @Override
             void process() {
@@ -735,7 +735,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setDataSource(@NonNull DataSourceDesc dsd) {
+    public @NonNull Object setDataSource(@NonNull DataSourceDesc dsd) {
         return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
@@ -768,7 +768,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setNextDataSource(@NonNull DataSourceDesc dsd) {
+    public @NonNull Object setNextDataSource(@NonNull DataSourceDesc dsd) {
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
@@ -791,7 +791,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
+    public @NonNull Object setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
@@ -853,7 +853,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object clearNextDataSources() {
+    public @NonNull Object clearNextDataSources() {
         return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
@@ -1194,7 +1194,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object loopCurrent(boolean loop) {
+    public @NonNull Object loopCurrent(boolean loop) {
         return addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
             @Override
             void process() {
@@ -1216,7 +1216,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setPlayerVolume(float volume) {
+    public @NonNull Object setPlayerVolume(float volume) {
         return addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
             @Override
             void process() {
@@ -1257,7 +1257,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object notifyWhenCommandLabelReached(@NonNull Object label) {
+    public @NonNull Object notifyWhenCommandLabelReached(@NonNull Object label) {
         return addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
             @Override
             void process() {
@@ -1285,7 +1285,7 @@
      * @param sh the SurfaceHolder to use for video display
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
-    public Object setDisplay(SurfaceHolder sh) {
+    public @NonNull Object setDisplay(@Nullable SurfaceHolder sh) {
         return addTask(new Task(CALL_COMPLETED_SET_DISPLAY, false) {
             @Override
             void process() {
@@ -1321,7 +1321,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setSurface(Surface surface) {
+    public @NonNull Object setSurface(@Nullable Surface surface) {
         return addTask(new Task(CALL_COMPLETED_SET_SURFACE, false) {
             @Override
             void process() {
@@ -1355,7 +1355,7 @@
      * @see android.os.PowerManager
      */
     // This is an asynchronous call.
-    public Object setWakeLock(@NonNull PowerManager.WakeLock wakeLock) {
+    public @NonNull Object setWakeLock(@NonNull PowerManager.WakeLock wakeLock) {
         return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
             @Override
             void process() {
@@ -1390,7 +1390,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setScreenOnWhilePlaying(boolean screenOn) {
+    public @NonNull Object setScreenOnWhilePlaying(boolean screenOn) {
         return addTask(new Task(CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING, false) {
             @Override
             void process() {
@@ -1466,7 +1466,7 @@
      */
     // This is a synchronous call.
     @Override
-    public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
+    public boolean setPreferredDevice(@Nullable AudioDeviceInfo deviceInfo) {
         boolean status = native_setPreferredDevice(deviceInfo);
         if (status) {
             synchronized (this) {
@@ -1483,7 +1483,7 @@
      * is not guaranteed to correspond to the actual device being used for playback.
      */
     @Override
-    public AudioDeviceInfo getPreferredDevice() {
+    public @Nullable AudioDeviceInfo getPreferredDevice() {
         synchronized (this) {
             return mPreferredDevice;
         }
@@ -1496,7 +1496,7 @@
      * selected device when the player was last active.
      */
     @Override
-    public native AudioDeviceInfo getRoutedDevice();
+    public @Nullable native AudioDeviceInfo getRoutedDevice();
 
     /**
      * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
@@ -1508,8 +1508,8 @@
      */
     // This is a synchronous call.
     @Override
-    public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
-            Handler handler) {
+    public void addOnRoutingChangedListener(@NonNull AudioRouting.OnRoutingChangedListener listener,
+            @Nullable Handler handler) {
         if (listener == null) {
             throw new IllegalArgumentException("addOnRoutingChangedListener: listener is NULL");
         }
@@ -1527,7 +1527,8 @@
      */
     // This is a synchronous call.
     @Override
-    public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
+    public void removeOnRoutingChangedListener(
+            @NonNull AudioRouting.OnRoutingChangedListener listener) {
         if (listener == null) {
             throw new IllegalArgumentException("removeOnRoutingChangedListener: listener is NULL");
         }
@@ -1547,7 +1548,7 @@
      * notification {@code EventCallback.onVideoSizeChanged} when the size
      * is available.
      */
-    public Size getVideoSize() {
+    public @NonNull Size getVideoSize() {
         return mVideoSize;
     }
 
@@ -1560,7 +1561,7 @@
      *
      * Additional vendor-specific fields may also be present in the return value.
      */
-    public PersistableBundle getMetrics() {
+    public @Nullable PersistableBundle getMetrics() {
         PersistableBundle bundle = native_getMetrics();
         return bundle;
     }
@@ -1592,7 +1593,7 @@
      */
     // TODO: make it public when ready
     // This is an asynchronous call.
-    Object setBufferingParams(@NonNull BufferingParams params) {
+    @NonNull Object setBufferingParams(@NonNull BufferingParams params) {
         return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
@@ -1614,7 +1615,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setPlaybackParams(@NonNull PlaybackParams params) {
+    public @NonNull Object setPlaybackParams(@NonNull PlaybackParams params) {
         return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
             @Override
             void process() {
@@ -1642,7 +1643,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setSyncParams(@NonNull SyncParams params) {
+    public @NonNull Object setSyncParams(@NonNull SyncParams params) {
         return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
             @Override
             void process() {
@@ -1671,7 +1672,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object seekTo(long msec) {
+    public @NonNull Object seekTo(long msec) {
         return seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
     }
 
@@ -1745,7 +1746,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object seekTo(long msec, @SeekMode int mode) {
+    public @NonNull Object seekTo(long msec, @SeekMode int mode) {
         return addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
             @Override
             void process() {
@@ -1846,7 +1847,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setAudioSessionId(int sessionId) {
+    public @NonNull Object setAudioSessionId(int sessionId) {
         final AudioTrack dummyAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
                     AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2,
                     AudioTrack.MODE_STATIC, sessionId);
@@ -1887,7 +1888,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object attachAuxEffect(int effectId) {
+    public @NonNull Object attachAuxEffect(int effectId) {
         return addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
             @Override
             void process() {
@@ -1912,7 +1913,7 @@
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
-    public Object setAuxEffectSendLevel(float level) {
+    public @NonNull Object setAuxEffectSendLevel(float level) {
         return addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
             @Override
             void process() {
@@ -1984,7 +1985,7 @@
          * When the language is unknown or could not be determined,
          * ISO-639-2 language code, "und", is returned.
          */
-        public String getLanguage() {
+        public @Nullable String getLanguage() {
             String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
             return language == null ? "und" : language;
         }
@@ -1993,7 +1994,7 @@
          * Gets the {@link MediaFormat} of the track.  If the format is
          * unknown or could not be determined, null is returned.
          */
-        public MediaFormat getFormat() {
+        public @Nullable MediaFormat getFormat() {
             if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
                     || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
                 return mFormat;
@@ -2201,7 +2202,7 @@
      * @see MediaPlayer2#getTrackInfo()
      */
     // This is an asynchronous call.
-    public Object selectTrack(int index) {
+    public @NonNull Object selectTrack(int index) {
         return selectTrack(getCurrentDataSource(), index);
     }
 
@@ -2235,7 +2236,7 @@
      * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
      */
     // This is an asynchronous call.
-    public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
+    public @NonNull Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
         return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
             @Override
             void process() {
@@ -2257,7 +2258,7 @@
      * @see MediaPlayer2#getTrackInfo()
      */
     // This is an asynchronous call.
-    public Object deselectTrack(int index) {
+    public @NonNull Object deselectTrack(int index) {
         return deselectTrack(getCurrentDataSource(), index);
     }
 
@@ -2277,7 +2278,7 @@
      * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
      */
     // This is an asynchronous call.
-    public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
+    public @NonNull Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
         return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
             @Override
             void process() {
@@ -2928,7 +2929,7 @@
      * @param eventCallback the callback to be unregistered
      */
     // This is a synchronous call.
-    public void unregisterEventCallback(EventCallback eventCallback) {
+    public void unregisterEventCallback(@NonNull EventCallback eventCallback) {
         synchronized (mEventCbLock) {
             for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
                 if (cb.second == eventCallback) {
@@ -3850,7 +3851,7 @@
      */
     // This is an asynchronous call.
     @TestApi
-    public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
+    public @NonNull Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
         return addTask(newPrepareDrmTask(dsd, uuid));
     }
 
diff --git a/media/apex/java/android/media/UriDataSourceDesc.java b/media/apex/java/android/media/UriDataSourceDesc.java
index 4eb9e8d..c0b3c82 100644
--- a/media/apex/java/android/media/UriDataSourceDesc.java
+++ b/media/apex/java/android/media/UriDataSourceDesc.java
@@ -51,7 +51,7 @@
      * Return the Uri of this data source.
      * @return the Uri of this data source
      */
-    public Uri getUri() {
+    public @NonNull Uri getUri() {
         return mUri;
     }
 
@@ -59,7 +59,7 @@
      * Return the Uri headers of this data source.
      * @return the Uri headers of this data source
      */
-    public Map<String, String> getHeaders() {
+    public @Nullable Map<String, String> getHeaders() {
         if (mHeader == null) {
             return null;
         }
@@ -70,7 +70,7 @@
      * Return the Uri cookies of this data source.
      * @return the Uri cookies of this data source
      */
-    public List<HttpCookie> getCookies() {
+    public @Nullable List<HttpCookie> getCookies() {
         if (mCookies == null) {
             return null;
         }
@@ -81,7 +81,7 @@
      * Return the Context used for resolving the Uri of this data source.
      * @return the Context used for resolving the Uri of this data source
      */
-    public Context getContext() {
+    public @NonNull Context getContext() {
         return mContext;
     }
 
@@ -117,7 +117,7 @@
          * @param dsd the {@link UriDataSourceDesc} object whose data will be reused
          * in the new Builder.
          */
-        public Builder(UriDataSourceDesc dsd) {
+        public Builder(@Nullable UriDataSourceDesc dsd) {
             super(dsd);
             if (dsd == null) {
                 return;  // use default
@@ -136,6 +136,11 @@
          * @return a new {@link UriDataSourceDesc} object
          */
         public @NonNull UriDataSourceDesc build() {
+            if (mUri == null || mContext == null) {
+                throw new IllegalStateException(
+                        "Uri and Context should not be null");
+            }
+
             UriDataSourceDesc dsd = new UriDataSourceDesc();
             super.build(dsd);
             dsd.mUri = mUri;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 46d4204..aed8e4e 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -630,7 +630,7 @@
          *                     true to allow apps to capture the audio
          * @return the same Builder instance
          */
-        public Builder setAllowCapture(boolean allowCapture) {
+        public @NonNull Builder setAllowCapture(boolean allowCapture) {
             if (allowCapture) {
                 mFlags &= ~FLAG_NO_CAPTURE;
             } else {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 15f9b47..669baea 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4068,6 +4068,10 @@
      /**
      * Indicate Hearing Aid connection state change and eventually suppress
      * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+     * This operation is asynchronous but its execution will still be sequentially scheduled
+     * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+     * * BluetoothDevice, int, int, boolean, int)} and
+     * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
      * @param device Bluetooth device connected/disconnected
      * @param state new connection state (BluetoothProfile.STATE_xxx)
      * @param musicDevice Default get system volume for the connecting device.
@@ -4075,27 +4079,27 @@
      * {@link android.bluetooth.BluetoothProfile.HEARING_AID})
      * @param suppressNoisyIntent if true the
      * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
-     * @return a delay in ms that the caller should wait before broadcasting
-     * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent.
      * {@hide}
      */
-    public int setBluetoothHearingAidDeviceConnectionState(
+    public void setBluetoothHearingAidDeviceConnectionState(
                 BluetoothDevice device, int state, boolean suppressNoisyIntent,
                 int musicDevice) {
         final IAudioService service = getService();
-        int delay = 0;
         try {
-            delay = service.setBluetoothHearingAidDeviceConnectionState(device,
+            service.setBluetoothHearingAidDeviceConnectionState(device,
                 state, suppressNoisyIntent, musicDevice);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        return delay;
     }
 
      /**
      * Indicate A2DP source or sink connection state change and eventually suppress
      * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+     * This operation is asynchronous but its execution will still be sequentially scheduled
+     * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice,
+     * int, boolean, int)} and
+     * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
      * @param device Bluetooth device connected/disconnected
      * @param state  new connection state, {@link BluetoothProfile#STATE_CONNECTED}
      *     or {@link BluetoothProfile#STATE_DISCONNECTED}
@@ -4105,26 +4109,27 @@
      * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
      * @param suppressNoisyIntent if true the
      * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
-     * @return a delay in ms that the caller should wait before broadcasting
-     * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
      * {@hide}
      */
-    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
             BluetoothDevice device, int state,
             int profile, boolean suppressNoisyIntent, int a2dpVolume) {
         final IAudioService service = getService();
-        int delay = 0;
         try {
-            delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+            service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
                 state, profile, suppressNoisyIntent, a2dpVolume);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        return delay;
     }
 
      /**
      * Indicate A2DP device configuration has changed.
+     * This operation is asynchronous but its execution will still be sequentially scheduled
+     * relative to calls to
+     * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int,
+     * boolean, int)} and
+     * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)}
      * @param device Bluetooth device whose configuration has changed.
      * {@hide}
      */
diff --git a/media/java/android/media/AudioPlaybackCaptureConfiguration.java b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
index 9a16aea..333cd2d 100644
--- a/media/java/android/media/AudioPlaybackCaptureConfiguration.java
+++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
@@ -72,7 +72,7 @@
      *
      * @param audioFormat The format in which to capture the audio.
      */
-    AudioMix createAudioMix(AudioFormat audioFormat) {
+    @NonNull AudioMix createAudioMix(@NonNull AudioFormat audioFormat) {
         return new AudioMix.Builder(mAudioMixingRule)
                 .setFormat(audioFormat)
                 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER)
@@ -123,7 +123,7 @@
          * @throws IllegalStateException if called in conjunction with
          *     {@link #excludeUsage(AudioAttributes)}.
          */
-        public Builder addMatchingUsage(@NonNull AudioAttributes audioAttributes) {
+        public @NonNull Builder addMatchingUsage(@NonNull AudioAttributes audioAttributes) {
             Preconditions.checkNotNull(audioAttributes);
             Preconditions.checkState(
                     mUsageMatchType != MATCH_TYPE_EXCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES);
@@ -141,7 +141,7 @@
          *
          * @throws IllegalStateException if called in conjunction with {@link #excludeUid(int)}.
          */
-        public Builder addMatchingUid(int uid) {
+        public @NonNull Builder addMatchingUid(int uid) {
             Preconditions.checkState(
                     mUidMatchType != MATCH_TYPE_EXCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES);
             mAudioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_UID, uid);
@@ -158,7 +158,7 @@
          * @throws IllegalStateException if called in conjunction with
          *     {@link #addMatchingUsage(AudioAttributes)}.
          */
-        public Builder excludeUsage(@NonNull AudioAttributes audioAttributes) {
+        public @NonNull Builder excludeUsage(@NonNull AudioAttributes audioAttributes) {
             Preconditions.checkNotNull(audioAttributes);
             Preconditions.checkState(
                     mUsageMatchType != MATCH_TYPE_INCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES);
@@ -176,7 +176,7 @@
          *
          * @throws IllegalStateException if called in conjunction with {@link #addMatchingUid(int)}.
          */
-        public Builder excludeUid(int uid) {
+        public @NonNull Builder excludeUid(int uid) {
             Preconditions.checkState(
                     mUidMatchType != MATCH_TYPE_INCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES);
             mAudioMixingRuleBuilder.excludeMixRule(AudioMixingRule.RULE_MATCH_UID, uid);
@@ -189,7 +189,7 @@
          *
          * @throws UnsupportedOperationException if the parameters set are incompatible.
          */
-        public AudioPlaybackCaptureConfiguration build() {
+        public @NonNull AudioPlaybackCaptureConfiguration build() {
             return new AudioPlaybackCaptureConfiguration(mAudioMixingRuleBuilder.build(),
                                                          mProjection);
         }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 3d5120f..28937a6 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -618,7 +618,7 @@
          * @throws IllegalStateException if called in conjunction with {@link #setAudioSource(int)}.
          * @throws NullPointerException if {@code config} is null.
          */
-        public Builder setAudioPlaybackCaptureConfig(
+        public @NonNull Builder setAudioPlaybackCaptureConfig(
                 @NonNull AudioPlaybackCaptureConfiguration config) {
             Preconditions.checkNotNull(
                     config, "Illegal null AudioPlaybackCaptureConfiguration argument");
@@ -647,7 +647,7 @@
             return this;
         }
 
-        private AudioRecord buildAudioPlaybackCaptureRecord() {
+        private @NonNull AudioRecord buildAudioPlaybackCaptureRecord() {
             AudioMix audioMix = mAudioPlaybackCaptureConfiguration.createAudioMix(mFormat);
             MediaProjection projection = mAudioPlaybackCaptureConfiguration.getMediaProjection();
             AudioPolicy audioPolicy = new AudioPolicy.Builder(/*context=*/ null)
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 3bb696b..7cfe0dd 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -3168,7 +3168,8 @@
          * @param sizeInFrames the number of frames available to write without blocking.
          *   Note that the frame size of a compressed stream is 1 byte.
          */
-        public void onDataRequest(@NonNull AudioTrack track, int sizeInFrames) { }
+        public void onDataRequest(@NonNull AudioTrack track, @IntRange(from = 0) int sizeInFrames) {
+        }
     }
 
     /**
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index 8bdb8a6..d9be0a5 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -165,7 +165,7 @@
      * If the audio attributes are not set with {@link #setAudioAttributes(AudioAttributes)},
      * attributes comprising {@link AudioAttributes#USAGE_MEDIA} will be used.
      */
-    public static class Builder {
+    public static final class Builder {
         private AudioAttributes mAudioAttributes;
         private AudioDeviceInfo mAudioDeviceInfo;
 
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 1b82fcc..0285667 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -228,10 +228,10 @@
 
     oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
 
-    int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
+    void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
             int state, boolean suppressNoisyIntent, int musicDevice);
 
-    int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+    void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
             int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);
 
     oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
diff --git a/media/java/android/media/MediaTimestamp.java b/media/java/android/media/MediaTimestamp.java
index 03e454c..da3362a 100644
--- a/media/java/android/media/MediaTimestamp.java
+++ b/media/java/android/media/MediaTimestamp.java
@@ -134,7 +134,7 @@
      * @hide
      */
     @SystemApi
-    public static class Builder {
+    public static final class Builder {
         long mMediaTimeUs;
         long mNanoTime;
         float mClockRate = 1.0f;
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index fbe5152..b302b06 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -571,13 +571,12 @@
     // Utilities
 
     /**
+     * @hide
      * Use to generate warning or exception in legacy code paths that allowed passing stream types
      * to qualify audio playback.
      * @param streamType the stream type to check
      * @throws IllegalArgumentException
-     * @deprecated This method is not intended to be used by applications.
      */
-    @java.lang.Deprecated
     public static void deprecateStreamTypeForPlayback(int streamType, @NonNull String className,
             @NonNull String opName) throws IllegalArgumentException {
         // STREAM_ACCESSIBILITY was introduced at the same time the use of stream types
diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java
index 852babe..31622a9 100644
--- a/media/java/android/media/SubtitleData.java
+++ b/media/java/android/media/SubtitleData.java
@@ -155,7 +155,7 @@
      * @hide
      */
     @SystemApi
-    public static class Builder {
+    public static final class Builder {
         private int mTrackIndex;
         private long mStartTimeUs;
         private long mDurationUs;
diff --git a/media/java/android/media/TimedMetaData.java b/media/java/android/media/TimedMetaData.java
index 2a89888..990760c 100644
--- a/media/java/android/media/TimedMetaData.java
+++ b/media/java/android/media/TimedMetaData.java
@@ -104,7 +104,7 @@
      * @hide
      */
     @SystemApi
-    public static class Builder {
+    public static final class Builder {
         private long mTimestampUs;
         private byte[] mMetaData = new byte[0];
 
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.java b/media/java/android/media/audiopolicy/AudioProductStrategies.java
index 98b6d97..c305b68 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategies.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategies.java
@@ -85,7 +85,7 @@
      * Returns an {@link Iterator}
      */
     @Override
-    public Iterator<AudioProductStrategy> iterator() {
+    public @NonNull Iterator<AudioProductStrategy> iterator() {
         return mAudioProductStrategyList.iterator();
     }
 
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
index 0b4ba93..b60947f 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
@@ -77,7 +77,7 @@
     /**
      * @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}.
      */
-    public List<AudioAttributes> getAudioAttributes() {
+    public @NonNull List<AudioAttributes> getAudioAttributes() {
         return Arrays.asList(mAudioAttributes);
     }
 
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroups.java b/media/java/android/media/audiopolicy/AudioVolumeGroups.java
index 301bec7..2e56f84 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroups.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroups.java
@@ -70,7 +70,7 @@
      * Returns an {@link Iterator}
      */
     @Override
-    public Iterator<AudioVolumeGroup> iterator() {
+    public @NonNull Iterator<AudioVolumeGroup> iterator() {
         return mAudioVolumeGroupList.iterator();
     }
 
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index fb473f0..167d255 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -9,7 +9,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     mockito-target-minus-junit4 \
-    android-support-test \
+    androidx.test.rules \
     android-ex-camera2
 
 LOCAL_PACKAGE_NAME := mediaframeworktest
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index e50a375..fb2d630 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -86,7 +86,7 @@
             android:label="MediaFramework integration tests InstrumentationRunner">
     </instrumentation>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.mediaframeworktest"
             android:label="media framework tests">
     </instrumentation>
diff --git a/media/tests/MediaFrameworkTest/AndroidTest.xml b/media/tests/MediaFrameworkTest/AndroidTest.xml
index 204959f..132028c 100644
--- a/media/tests/MediaFrameworkTest/AndroidTest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidTest.xml
@@ -21,7 +21,7 @@
     <option name="test-tag" value="MediaFrameworkTest" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.mediaframeworktest" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index d8c975f..87a59df 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -309,6 +309,10 @@
             Log.v(TAG, String.format("Camera %s has torch status changed to 0x%x",
                     cameraId, status));
         }
+        @Override
+        public void onCameraAccessPrioritiesChanged() {
+            Log.v(TAG, "Camera access permission change");
+        }
     }
 
     /**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
index feac63d..701454c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
@@ -28,7 +28,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.mtp.MtpConstants;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import libcore.net.MimeUtils;
 
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
index 7755cbc..719417e 100644
--- a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
@@ -114,6 +114,7 @@
     private NotificationManager mNM;
 
     private long mSystemSize;
+    private long mUserdataSize;
     private long mInstalledSize;
     private boolean mJustCancelledByUser;
 
@@ -220,9 +221,11 @@
 
         String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL);
         mSystemSize = intent.getLongExtra(DynamicAndroidClient.KEY_SYSTEM_SIZE, 0);
-        long userdata = intent.getLongExtra(DynamicAndroidClient.KEY_USERDATA_SIZE, 0);
+        mUserdataSize = intent.getLongExtra(DynamicAndroidClient.KEY_USERDATA_SIZE, 0);
 
-        mInstallTask = new InstallationAsyncTask(url, mSystemSize, userdata, mDynAndroid, this);
+        mInstallTask = new InstallationAsyncTask(
+                url, mSystemSize, mUserdataSize, mDynAndroid, this);
+
         mInstallTask.execute();
 
         // start fore ground
@@ -332,8 +335,8 @@
             case STATUS_IN_PROGRESS:
                 builder.setContentText(getString(R.string.notification_install_inprogress));
 
-                int max = (int) Math.max(mSystemSize >> 20, 1);
-                int progress = (int) mInstalledSize >> 20;
+                int max = (int) Math.max((mSystemSize + mUserdataSize) >> 20, 1);
+                int progress = (int) (mInstalledSize >> 20);
 
                 builder.setProgress(max, progress, false);
 
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
index 3c759e9..03fc773 100644
--- a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
@@ -16,6 +16,7 @@
 
 package com.android.dynandroid;
 
+import android.gsi.GsiProgress;
 import android.os.AsyncTask;
 import android.os.DynamicAndroidManager;
 import android.util.Log;
@@ -63,8 +64,6 @@
     private final InstallStatusListener mListener;
     private DynamicAndroidManager.Session mInstallationSession;
 
-    private long mInstalledSize;
-    private long mReportedInstalledSize;
     private int mResult = NO_RESULT;
 
     private InputStream mStream;
@@ -89,8 +88,40 @@
         Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
 
         try {
-            // call start in background
-            mInstallationSession = mDynamicAndroid.startInstallation(mSystemSize, mUserdataSize);
+            long installedSize = 0;
+            long reportedInstalledSize = 0;
+
+            long minStepToReport = (mSystemSize + mUserdataSize) / 100;
+
+            // init input stream before calling startInstallation(), which takes 90 seconds.
+            initInputStream();
+
+            Thread thread = new Thread(() -> {
+                mInstallationSession =
+                        mDynamicAndroid.startInstallation(mSystemSize, mUserdataSize);
+            });
+
+
+            thread.start();
+
+            while (thread.isAlive()) {
+                if (isCancelled()) {
+                    boolean aborted = mDynamicAndroid.abort();
+                    Log.d(TAG, "Called DynamicAndroidManager.abort(), result = " + aborted);
+                    return RESULT_OK;
+                }
+
+                GsiProgress progress = mDynamicAndroid.getInstallationProgress();
+                installedSize = progress.bytes_processed;
+
+                if (installedSize > reportedInstalledSize + minStepToReport) {
+                    publishProgress(installedSize);
+                    reportedInstalledSize = installedSize;
+                }
+
+                Thread.sleep(10);
+            }
+
 
             if (mInstallationSession == null) {
                 Log.e(TAG, "Failed to start installation with requested size: "
@@ -99,12 +130,11 @@
                 return RESULT_ERROR_IO;
             }
 
-            initInputStream();
+            installedSize = mUserdataSize;
 
             byte[] bytes = new byte[READ_BUFFER_SIZE];
 
             int numBytesRead;
-            long minStepToReport = mSystemSize / 100;
 
             Log.d(TAG, "Start installation loop");
             while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
@@ -119,11 +149,11 @@
                     throw new IOException("Failed write() to DynamicAndroid");
                 }
 
-                mInstalledSize += numBytesRead;
+                installedSize += numBytesRead;
 
-                if (mInstalledSize > mReportedInstalledSize + minStepToReport) {
-                    publishProgress(mInstalledSize);
-                    mReportedInstalledSize = mInstalledSize;
+                if (installedSize > reportedInstalledSize + minStepToReport) {
+                    publishProgress(installedSize);
+                    reportedInstalledSize = installedSize;
                 }
             }
 
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote.xml b/packages/SettingsLib/res/drawable/ic_volume_remote.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_volume_remote.xml
rename to packages/SettingsLib/res/drawable/ic_volume_remote.xml
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml b/packages/SettingsLib/res/drawable/ic_volume_remote_mute.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
rename to packages/SettingsLib/res/drawable/ic_volume_remote_mute.xml
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index 4ac3ce4..09107ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -48,6 +48,11 @@
     // Keep last 24 hours of location app information.
     private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
 
+    /** The flags for querying ops that are trusted for showing in the UI. */
+    public static final int TRUSTED_STATE_FLAGS = AppOpsManager.OP_FLAG_SELF
+            | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
+            | AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+
     @VisibleForTesting
     static final int[] LOCATION_OPS = new int[]{
             AppOpsManager.OP_FINE_LOCATION,
@@ -136,8 +141,7 @@
         // Earliest time for a location access to end and still be shown in list.
         long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
         for (AppOpsManager.OpEntry entry : entries) {
-            locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(),
-                    entry.getLastAccessForegroundTime());
+            locationAccessFinishTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
         }
         // Bail out if the entry is out of date.
         if (locationAccessFinishTime < recentLocationCutoffTime) {
diff --git a/core/java/android/service/textclassifier/ITextLinksCallback.aidl b/packages/SettingsLib/src/com/android/settingslib/volume/D.java
similarity index 65%
rename from core/java/android/service/textclassifier/ITextLinksCallback.aidl
rename to packages/SettingsLib/src/com/android/settingslib/volume/D.java
index a9e0dde..7e0654d 100644
--- a/core/java/android/service/textclassifier/ITextLinksCallback.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/D.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,15 +14,10 @@
  * limitations under the License.
  */
 
-package android.service.textclassifier;
+package com.android.settingslib.volume;
 
-import android.view.textclassifier.TextLinks;
+import android.util.Log;
 
-/**
- * Callback for a TextLinks request.
- * @hide
- */
-oneway interface ITextLinksCallback {
-    void onSuccess(in TextLinks links);
-    void onFailure();
-}
\ No newline at end of file
+class D {
+    public static boolean BUG = Log.isLoggable("volume", Log.DEBUG);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
rename to packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
index 8b00eee..4ed1154 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.volume;
+package com.android.settingslib.volume;
 
 import android.app.PendingIntent;
 import android.content.Context;
@@ -27,7 +27,6 @@
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.MediaSession;
 import android.media.session.MediaSession.QueueItem;
 import android.media.session.MediaSession.Token;
 import android.media.session.MediaSessionManager;
@@ -41,7 +40,6 @@
 import android.util.Log;
 
 import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -73,16 +71,24 @@
         mCallbacks = callbacks;
     }
 
+    /**
+     * Dump to {@code writer}
+     */
     public void dump(PrintWriter writer) {
         writer.println(getClass().getSimpleName() + " state:");
-        writer.print("  mInit: "); writer.println(mInit);
-        writer.print("  mRecords.size: "); writer.println(mRecords.size());
+        writer.print("  mInit: ");
+        writer.println(mInit);
+        writer.print("  mRecords.size: ");
+        writer.println(mRecords.size());
         int i = 0;
         for (MediaControllerRecord r : mRecords.values()) {
             dump(++i, writer, r.controller);
         }
     }
 
+    /**
+     * init MediaSessions
+     */
     public void init() {
         if (D.BUG) Log.d(TAG, "init");
         // will throw if no permission
@@ -97,12 +103,18 @@
         mHandler.sendEmptyMessage(H.UPDATE_SESSIONS);
     }
 
+    /**
+     * Destroy MediaSessions
+     */
     public void destroy() {
         if (D.BUG) Log.d(TAG, "destroy");
         mInit = false;
         mMgr.removeOnActiveSessionsChangedListener(mSessionsListener);
     }
 
+    /**
+     * Set volume {@code level} to remote media {@code token}
+     */
     public void setVolume(Token token, int level) {
         final MediaControllerRecord r = mRecords.get(token);
         if (r == null) {
@@ -113,15 +125,17 @@
         r.controller.setVolumeTo(level, 0);
     }
 
-    private void onRemoteVolumeChangedH(MediaSession.Token sessionToken, int flags) {
+    private void onRemoteVolumeChangedH(Token sessionToken, int flags) {
         final MediaController controller = new MediaController(mContext, sessionToken);
-        if (D.BUG) Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
-                + Util.audioManagerFlagsToString(flags));
+        if (D.BUG) {
+            Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
+                    + Util.audioManagerFlagsToString(flags));
+        }
         final Token token = controller.getSessionToken();
         mCallbacks.onRemoteVolumeChanged(token, flags);
     }
 
-    private void onUpdateRemoteControllerH(MediaSession.Token sessionToken) {
+    private void onUpdateRemoteControllerH(Token sessionToken) {
         final MediaController controller =
                 sessionToken != null ? new MediaController(mContext, sessionToken) : null;
         final String pkg = controller != null ? controller.getPackageName() : null;
@@ -191,7 +205,8 @@
             if (appLabel.length() > 0) {
                 return appLabel;
             }
-        } catch (NameNotFoundException e) { }
+        } catch (NameNotFoundException e) {
+        }
         return pkg;
     }
 
@@ -240,29 +255,11 @@
         }
     }
 
-    public static void dumpMediaSessions(Context context) {
-        final MediaSessionManager mgr = (MediaSessionManager) context
-                .getSystemService(Context.MEDIA_SESSION_SERVICE);
-        try {
-            final List<MediaController> controllers = mgr.getActiveSessions(null);
-            final int N = controllers.size();
-            if (D.BUG) Log.d(TAG, N + " controllers");
-            for (int i = 0; i < N; i++) {
-                final StringWriter sw = new StringWriter();
-                final PrintWriter pw = new PrintWriter(sw, true);
-                dump(i + 1, pw, controllers.get(i));
-                if (D.BUG) Log.d(TAG, sw.toString());
-            }
-        } catch (SecurityException e) {
-            Log.w(TAG, "Not allowed to get sessions", e);
-        }
-    }
-
     private final class MediaControllerRecord extends MediaController.Callback {
-        private final MediaController controller;
+        public final MediaController controller;
 
-        private boolean sentRemote;
-        private String name;
+        public boolean sentRemote;
+        public String name;
 
         private MediaControllerRecord(MediaController controller) {
             this.controller = controller;
@@ -274,8 +271,10 @@
 
         @Override
         public void onAudioInfoChanged(PlaybackInfo info) {
-            if (D.BUG) Log.d(TAG, cb("onAudioInfoChanged") + Util.playbackInfoToString(info)
-                    + " sentRemote=" + sentRemote);
+            if (D.BUG) {
+                Log.d(TAG, cb("onAudioInfoChanged") + Util.playbackInfoToString(info)
+                        + " sentRemote=" + sentRemote);
+            }
             final boolean remote = isRemote(info);
             if (!remote && sentRemote) {
                 mCallbacks.onRemoteRemoved(controller.getSessionToken());
@@ -324,22 +323,22 @@
 
     private final OnActiveSessionsChangedListener mSessionsListener =
             new OnActiveSessionsChangedListener() {
-        @Override
-        public void onActiveSessionsChanged(List<MediaController> controllers) {
-            onActiveSessionsUpdatedH(controllers);
-        }
-    };
+                @Override
+                public void onActiveSessionsChanged(List<MediaController> controllers) {
+                    onActiveSessionsUpdatedH(controllers);
+                }
+            };
 
     private final IRemoteVolumeController mRvc = new IRemoteVolumeController.Stub() {
         @Override
-        public void remoteVolumeChanged(MediaSession.Token sessionToken, int flags)
+        public void remoteVolumeChanged(Token sessionToken, int flags)
                 throws RemoteException {
             mHandler.obtainMessage(H.REMOTE_VOLUME_CHANGED, flags, 0,
                     sessionToken).sendToTarget();
         }
 
         @Override
-        public void updateRemoteController(final MediaSession.Token sessionToken)
+        public void updateRemoteController(final Token sessionToken)
                 throws RemoteException {
             mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, sessionToken).sendToTarget();
         }
@@ -361,18 +360,32 @@
                     onActiveSessionsUpdatedH(mMgr.getActiveSessions(null));
                     break;
                 case REMOTE_VOLUME_CHANGED:
-                    onRemoteVolumeChangedH((MediaSession.Token) msg.obj, msg.arg1);
+                    onRemoteVolumeChangedH((Token) msg.obj, msg.arg1);
                     break;
                 case UPDATE_REMOTE_CONTROLLER:
-                    onUpdateRemoteControllerH((MediaSession.Token) msg.obj);
+                    onUpdateRemoteControllerH((Token) msg.obj);
                     break;
             }
         }
     }
 
+    /**
+     * Callback for remote media sessions
+     */
     public interface Callbacks {
+        /**
+         * Invoked when remote media session is updated
+         */
         void onRemoteUpdate(Token token, String name, PlaybackInfo pi);
+
+        /**
+         * Invoked when remote media session is removed
+         */
         void onRemoteRemoved(Token t);
+
+        /**
+         * Invoked when remote volume is changed
+         */
         void onRemoteVolumeChanged(Token token, int flags);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/Util.java b/packages/SettingsLib/src/com/android/settingslib/volume/Util.java
new file mode 100644
index 0000000..4e770ae
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/Util.java
@@ -0,0 +1,187 @@
+/*
+ * 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.settingslib.volume;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.VolumeProvider;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.PlaybackState;
+import android.telephony.TelephonyManager;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * Static helpers for the volume dialog.
+ */
+public class Util {
+
+    private static final int[] AUDIO_MANAGER_FLAGS = new int[]{
+            AudioManager.FLAG_SHOW_UI,
+            AudioManager.FLAG_VIBRATE,
+            AudioManager.FLAG_PLAY_SOUND,
+            AudioManager.FLAG_ALLOW_RINGER_MODES,
+            AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
+            AudioManager.FLAG_SHOW_VIBRATE_HINT,
+            AudioManager.FLAG_SHOW_SILENT_HINT,
+            AudioManager.FLAG_FROM_KEY,
+            AudioManager.FLAG_SHOW_UI_WARNINGS,
+    };
+
+    private static final String[] AUDIO_MANAGER_FLAG_NAMES = new String[]{
+            "SHOW_UI",
+            "VIBRATE",
+            "PLAY_SOUND",
+            "ALLOW_RINGER_MODES",
+            "REMOVE_SOUND_AND_VIBRATE",
+            "SHOW_VIBRATE_HINT",
+            "SHOW_SILENT_HINT",
+            "FROM_KEY",
+            "SHOW_UI_WARNINGS",
+    };
+
+    /**
+     * Extract log tag from {@code c}
+     */
+    public static String logTag(Class<?> c) {
+        final String tag = "vol." + c.getSimpleName();
+        return tag.length() < 23 ? tag : tag.substring(0, 23);
+    }
+
+    /**
+     * Convert media metadata to string
+     */
+    public static String mediaMetadataToString(MediaMetadata metadata) {
+        if (metadata == null) return null;
+        return metadata.getDescription().toString();
+    }
+
+    /**
+     * Convert playback info to string
+     */
+    public static String playbackInfoToString(PlaybackInfo info) {
+        if (info == null) return null;
+        final String type = playbackInfoTypeToString(info.getPlaybackType());
+        final String vc = volumeProviderControlToString(info.getVolumeControl());
+        return String.format("PlaybackInfo[vol=%s,max=%s,type=%s,vc=%s],atts=%s",
+                info.getCurrentVolume(), info.getMaxVolume(), type, vc, info.getAudioAttributes());
+    }
+
+    /**
+     * Convert type of playback info to string
+     */
+    public static String playbackInfoTypeToString(int type) {
+        switch (type) {
+            case PlaybackInfo.PLAYBACK_TYPE_LOCAL:
+                return "LOCAL";
+            case PlaybackInfo.PLAYBACK_TYPE_REMOTE:
+                return "REMOTE";
+            default:
+                return "UNKNOWN_" + type;
+        }
+    }
+
+    /**
+     * Convert state of playback info to string
+     */
+    public static String playbackStateStateToString(int state) {
+        switch (state) {
+            case PlaybackState.STATE_NONE:
+                return "STATE_NONE";
+            case PlaybackState.STATE_STOPPED:
+                return "STATE_STOPPED";
+            case PlaybackState.STATE_PAUSED:
+                return "STATE_PAUSED";
+            case PlaybackState.STATE_PLAYING:
+                return "STATE_PLAYING";
+            default:
+                return "UNKNOWN_" + state;
+        }
+    }
+
+    /**
+     * Convert volume provider control to string
+     */
+    public static String volumeProviderControlToString(int control) {
+        switch (control) {
+            case VolumeProvider.VOLUME_CONTROL_ABSOLUTE:
+                return "VOLUME_CONTROL_ABSOLUTE";
+            case VolumeProvider.VOLUME_CONTROL_FIXED:
+                return "VOLUME_CONTROL_FIXED";
+            case VolumeProvider.VOLUME_CONTROL_RELATIVE:
+                return "VOLUME_CONTROL_RELATIVE";
+            default:
+                return "VOLUME_CONTROL_UNKNOWN_" + control;
+        }
+    }
+
+    /**
+     * Convert {@link PlaybackState} to string
+     */
+    public static String playbackStateToString(PlaybackState playbackState) {
+        if (playbackState == null) return null;
+        return playbackStateStateToString(playbackState.getState()) + " " + playbackState;
+    }
+
+    /**
+     * Convert audio manager flags to string
+     */
+    public static String audioManagerFlagsToString(int value) {
+        return bitFieldToString(value, AUDIO_MANAGER_FLAGS, AUDIO_MANAGER_FLAG_NAMES);
+    }
+
+    protected static String bitFieldToString(int value, int[] values, String[] names) {
+        if (value == 0) return "";
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < values.length; i++) {
+            if ((value & values[i]) != 0) {
+                if (sb.length() > 0) sb.append(',');
+                sb.append(names[i]);
+            }
+            value &= ~values[i];
+        }
+        if (value != 0) {
+            if (sb.length() > 0) sb.append(',');
+            sb.append("UNKNOWN_").append(value);
+        }
+        return sb.toString();
+    }
+
+    private static CharSequence emptyToNull(CharSequence str) {
+        return str == null || str.length() == 0 ? null : str;
+    }
+
+    /**
+     * Set text for specific {@link TextView}
+     */
+    public static boolean setText(TextView tv, CharSequence text) {
+        if (Objects.equals(emptyToNull(tv.getText()), emptyToNull(text))) return false;
+        tv.setText(text);
+        return true;
+    }
+
+    /**
+     * Return {@code true} if it is voice capable
+     */
+    public static boolean isVoiceCapable(Context context) {
+        final TelephonyManager telephony =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        return telephony != null && telephony.isVoiceCapable();
+    }
+}
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 d5b89ca..c1c5fa9 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,6 +17,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import android.util.LongSparseLongArray;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -152,11 +153,12 @@
     }
 
     private OpEntry createOpEntryWithTime(int op, long time) {
-        final long[] times = new long[AppOpsManager._NUM_UID_STATE];
         // Slot for background access timestamp.
-        times[AppOpsManager.UID_STATE_LAST_NON_RESTRICTED + 1] = time;
-        final long[] rejectTimes = new long[AppOpsManager._NUM_UID_STATE];
-        return new OpEntry(op, AppOpsManager.MODE_ALLOWED, times, rejectTimes, 0 /* duration */,
-                0 /* proxyUid */, "" /* proxyPackage */);
+        final LongSparseLongArray accessTimes = new LongSparseLongArray();
+        accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
+            AppOpsManager.OP_FLAG_SELF), time);
+
+        return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes, null /*durations*/,
+            null /*rejectTimes*/, null /* proxyUids */, null /* proxyPackages */);
     }
 }
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 08d5367..8bd5fd2 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.UserHandle;
 import android.os.UserManager;
 
+import android.util.LongSparseLongArray;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -153,6 +154,13 @@
     }
 
     private OpEntry createOpEntryWithTime(int op, long time, int duration) {
-        return new OpEntry(op, AppOpsManager.MODE_ALLOWED, time, 0L, duration, 0, "");
+        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);
+        return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes,
+            null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */);
     }
 }
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
deleted file mode 100644
index 035a4df..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
index 84c793f..672abf1 100644
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ b/packages/SystemUI/res/drawable/qs_detail_background.xml
@@ -14,6 +14,20 @@
     limitations under the License.
 -->
 <transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/qs_detail_transition" />
-    <item android:drawable="?android:attr/colorPrimary" />
+    <item>
+        <inset>
+            <shape>
+                <solid android:color="@color/qs_detail_transition"/>
+                <corners android:radius="?android:attr/dialogCornerRadius" />
+            </shape>
+        </inset>
+    </item>
+    <item>
+        <inset>
+            <shape>
+                <solid android:color="?android:attr/colorPrimary"/>
+                <corners android:radius="?android:attr/dialogCornerRadius" />
+            </shape>
+        </inset>
+    </item>
 </transition>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
index cdad94b..8dcddc2 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml
@@ -31,7 +31,7 @@
         android:paddingStart="48dp"
         android:paddingTop="43dp"
         android:text="@string/screen_pinning_title"
-        android:textColor="@color/screen_pinning_primary_text"
+        android:textColor="@android:color/white"
         android:textSize="24sp" />
 
     <TextView
@@ -43,7 +43,7 @@
         android:paddingStart="48dp"
         android:paddingTop="12.6dp"
         android:text="@string/screen_pinning_description"
-        android:textColor="@color/screen_pinning_primary_text"
+        android:textColor="@android:color/white"
         android:textSize="16sp" />
 
     <Button
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
index 586cdf3..716e127 100644
--- a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
+++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
@@ -1,27 +1,76 @@
 precision mediump float;
 
+// The actual wallpaper texture.
 uniform sampler2D uTexture;
-uniform float uCenterReveal;
+
+// The 85th percenile for the luminance histogram of the image (a value between 0 and 1).
+// This value represents the point in histogram that includes 85% of the pixels of the image.
+uniform float uPer85;
+
+// Reveal is the animation value that goes from 1 (the image is hidden) to 0 (the image is visible).
 uniform float uReveal;
+
+// The opacity of locked screen (constant value).
 uniform float uAod2Opacity;
 varying vec2 vTextureCoordinates;
 
+/*
+ * Calculates the relative luminance of the pixel.
+ */
 vec3 luminosity(vec3 color) {
     float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
     return vec3(lum);
 }
 
 vec4 transform(vec3 diffuse) {
-    // TODO: Add well comments here, tracking on b/123615467.
+    // Getting the luminance for this pixel
     vec3 lum = luminosity(diffuse);
-    diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal));
-    float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal));
-    diffuse = smoothstep(val, 1.0, diffuse);
-    diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal));
+
+    /*
+     * while the reveal > per85, it shows the luminance image (B&W image)
+     * then when moving passed that value, the image gets colored.
+     */
+    float trans = smoothstep(0., uPer85, uReveal);
+    diffuse = mix(diffuse, lum, trans);
+
+    // 'lower' value represents the capped 'reveal' value to the range [0, per85]
+    float selector = step(uPer85, uReveal);
+    float lower = mix(uReveal, uPer85, selector);
+
+    /*
+     * Remaps image:
+     * - from reveal=1 to reveal=per85 => lower=per85, diffuse=luminance
+     *   That means that remaps black and white image pixel
+     *   from a possible values of [0,1] to [per85, 1] (if the pixel is darker than per85,
+     *   it's gonna be black, if it's between per85 and 1, it's gonna be gray
+     *   and if it's 1 it's gonna be white).
+     * - from reveal=per85 to reveal=0 => lower=reveal, 'diffuse' changes from luminance to color
+     *   That means that remaps each image pixel color (rgb)
+     *   from a possible values of [0,1] to [lower, 1] (if the pixel color is darker than 'lower',
+     *   it's gonna be 0, if it's between 'lower' and 1, it's gonna be remap to a value
+     *   between 0 and 1 and if it's 1 it's gonna be 1).
+     * - if reveal=0 => lower=0, diffuse=color image
+     *   The image is shown as it is, colored.
+     */
+    vec3 remaps = smoothstep(lower, 1., diffuse);
+
+    // Interpolate between diffuse and remaps using reveal to avoid over saturation.
+    diffuse = mix(diffuse, remaps, uReveal);
+
+    /*
+     * Fades in the pixel value:
+     * - if reveal=1 => fadeInOpacity=0
+     * - from reveal=1 to reveal=per85 => 0<=fadeInOpacity<=1
+     * - if reveal>per85 => fadeInOpacity=1
+     */
+    float fadeInOpacity = 1. - smoothstep(uPer85, 1., uReveal);
+    diffuse *= uAod2Opacity * fadeInOpacity;
+
     return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
 }
 
 void main() {
+    // gets the pixel value of the wallpaper for this uv coordinates on screen.
     vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
     gl_FragColor = transform(fragColor.rgb);
 }
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/internal.xml b/packages/SystemUI/res/values/internal.xml
index e0d3cd2..930cfce 100644
--- a/packages/SystemUI/res/values/internal.xml
+++ b/packages/SystemUI/res/values/internal.xml
@@ -18,6 +18,5 @@
     <dimen name="status_bar_height">@*android:dimen/status_bar_height</dimen>
     <dimen name="navigation_bar_height">@*android:dimen/navigation_bar_height</dimen>
     <dimen name="navigation_bar_height_car_mode">@*android:dimen/navigation_bar_height_car_mode</dimen>
-    <color name="screen_pinning_primary_text">@*android:color/primary_text_default_material_light</color>
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 563b007..d40fa66 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,6 +1,7 @@
 package com.android.keyguard;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.WallpaperManager;
@@ -31,6 +32,9 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.TimeZone;
 
 /**
@@ -148,6 +152,7 @@
         Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
         Dependency.get(SysuiColorExtractor.class)
             .removeOnColorsChangedListener(mColorsListener);
+        setClockPlugin(null);
     }
 
     private void setClockPlugin(ClockPlugin plugin) {
@@ -332,6 +337,19 @@
         return mStateListener;
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyguardClockSwitch:");
+        pw.println("  mClockPlugin: " + mClockPlugin);
+        pw.println("  mClockView: " + mClockView);
+        pw.println("  mSmallClockFrame: " + mSmallClockFrame);
+        pw.println("  mBigClockContainer: " + mBigClockContainer);
+        pw.println("  mKeyguardStatusArea: " + mKeyguardStatusArea);
+        pw.println("  mDarkAmount: " + mDarkAmount);
+        pw.println("  mShowingHeader: " + mShowingHeader);
+        pw.println("  mSupportsDarkText: " + mSupportsDarkText);
+        pw.println("  mColorPalette: " + Arrays.toString(mColorPalette));
+    }
+
     /**
      * Special layout transition that scales the clock view as its bounds change, to make it look
      * like the text is shrinking.
@@ -371,11 +389,23 @@
                 boundsAnimator.addUpdateListener(animation -> {
                     float scale = MathUtils.lerp(startScale, 1f /* stop */,
                             animation.getAnimatedFraction());
-                    mClockView.setPivotX(mClockView.getWidth() / 2);
+                    mClockView.setPivotX(mClockView.getWidth() / 2f);
                     mClockView.setPivotY(0);
                     mClockView.setScaleX(scale);
                     mClockView.setScaleY(scale);
                 });
+                boundsAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        mClockView.setScaleX(1f);
+                        mClockView.setScaleY(1f);
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animator) {
+                        onAnimationEnd(animator);
+                    }
+                });
             }
 
             return animator;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 2040a76..8ebe1ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -64,6 +64,8 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -174,6 +176,7 @@
             if (mContentChangeListener != null) {
                 mContentChangeListener.run();
             }
+            Trace.endSection();
             return;
         }
 
@@ -375,6 +378,17 @@
         Trace.endSection();
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyguardSliceView:");
+        pw.println("  mClickActions: " + mClickActions);
+        pw.println("  mTitle: " + (mTitle == null ? "null" : mTitle.getVisibility() == VISIBLE));
+        pw.println("  mRow: " + (mRow == null ? "null" : mRow.getVisibility() == VISIBLE));
+        pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
+        pw.println("  mDarkAmount: " + mDarkAmount);
+        pw.println("  mSlice: " + mSlice);
+        pw.println("  mHasHeader: " + mHasHeader);
+    }
+
     public static class Row extends LinearLayout {
 
         /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 17546c5..808e264 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -41,6 +41,8 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Locale;
 import java.util.TimeZone;
 
@@ -289,6 +291,24 @@
         return false;
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyguardStatusView:");
+        pw.println("  mOwnerInfo: " + (mOwnerInfo == null
+                ? "null" : mOwnerInfo.getVisibility() == VISIBLE));
+        pw.println("  mPulsing: " + mPulsing);
+        pw.println("  mDarkAmount: " + mDarkAmount);
+        pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
+        if (mLogoutView != null) {
+            pw.println("  logout visible: " + (mLogoutView.getVisibility() == VISIBLE));
+        }
+        if (mClockView != null) {
+            mClockView.dump(fd, pw, args);
+        }
+        if (mKeyguardSlice != null) {
+            mKeyguardSlice.dump(fd, pw, args);
+        }
+    }
+
     // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
     // This is an optimization to ensure we only recompute the patterns when the inputs change.
     private static final class Patterns {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
index 19d85b1..a313336 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -50,7 +50,7 @@
 
     static final String A_POSITION = "aPosition";
     static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
-    static final String U_CENTER_REVEAL = "uCenterReveal";
+    static final String U_PER85 = "uPer85";
     static final String U_REVEAL = "uReveal";
     static final String U_AOD2OPACITY = "uAod2Opacity";
     static final String U_TEXTURE = "uTexture";
@@ -87,7 +87,7 @@
     private int mAttrPosition;
     private int mAttrTextureCoordinates;
     private int mUniAod2Opacity;
-    private int mUniCenterReveal;
+    private int mUniPer85;
     private int mUniReveal;
     private int mUniTexture;
     private int mTextureId;
@@ -131,7 +131,7 @@
 
     private void setupUniforms() {
         mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
-        mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL);
+        mUniPer85 = mProgram.getUniformHandle(U_PER85);
         mUniReveal = mProgram.getUniformHandle(U_REVEAL);
         mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
     }
@@ -144,8 +144,8 @@
                 return mAttrTextureCoordinates;
             case U_AOD2OPACITY:
                 return mUniAod2Opacity;
-            case U_CENTER_REVEAL:
-                return mUniCenterReveal;
+            case U_PER85:
+                return mUniPer85;
             case U_REVEAL:
                 return mUniReveal;
             case U_TEXTURE:
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 991b116..7295008 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -93,13 +93,13 @@
 
     @Override
     public void onDrawFrame(GL10 gl) {
-        float threshold = mImageProcessHelper.getPercentile85();
+        float per85 = mImageProcessHelper.getPercentile85();
         float reveal = mImageRevealHelper.getReveal();
 
         glClear(GL_COLOR_BUFFER_BIT);
 
         glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
-        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold);
+        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85);
         glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
 
         mWallpaper.useTexture();
diff --git a/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt b/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
new file mode 100644
index 0000000..d7a2d9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
@@ -0,0 +1,55 @@
+package com.android.systemui.power
+
+import com.android.systemui.power.PowerUI.NO_ESTIMATE_AVAILABLE
+
+/**
+ * A simple data class to snapshot battery state when a particular check for the
+ * low battery warning is running in the background.
+ */
+data class BatteryStateSnapshot(
+    val batteryLevel: Int,
+    val isPowerSaver: Boolean,
+    val plugged: Boolean,
+    val bucket: Int,
+    val batteryStatus: Int,
+    val severeLevelThreshold: Int,
+    val lowLevelThreshold: Int,
+    val timeRemainingMillis: Long,
+    val severeThresholdMillis: Long,
+    val lowThresholdMillis: Long,
+    val isBasedOnUsage: Boolean
+) {
+    /**
+     * Returns whether hybrid warning logic/copy should be used for this snapshot
+     */
+    var isHybrid: Boolean = false
+        private set
+
+    init {
+        this.isHybrid = true
+    }
+
+    constructor(
+        batteryLevel: Int,
+        isPowerSaver: Boolean,
+        plugged: Boolean,
+        bucket: Int,
+        batteryStatus: Int,
+        severeLevelThreshold: Int,
+        lowLevelThreshold: Int
+    ) : this(
+        batteryLevel,
+        isPowerSaver,
+        plugged,
+        bucket,
+        batteryStatus,
+        severeLevelThreshold,
+        lowLevelThreshold,
+        NO_ESTIMATE_AVAILABLE.toLong(),
+        NO_ESTIMATE_AVAILABLE.toLong(),
+        NO_ESTIMATE_AVAILABLE.toLong(),
+        false
+    ) {
+        this.isHybrid = false
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/Estimate.java b/packages/SystemUI/src/com/android/systemui/power/Estimate.java
deleted file mode 100644
index 12a8f0a..0000000
--- a/packages/SystemUI/src/com/android/systemui/power/Estimate.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.android.systemui.power;
-
-public class Estimate {
-    public final long estimateMillis;
-    public final boolean isBasedOnUsage;
-
-    public Estimate(long estimateMillis, boolean isBasedOnUsage) {
-        this.estimateMillis = estimateMillis;
-        this.isBasedOnUsage = isBasedOnUsage;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/power/Estimate.kt b/packages/SystemUI/src/com/android/systemui/power/Estimate.kt
new file mode 100644
index 0000000..dca0d45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/Estimate.kt
@@ -0,0 +1,3 @@
+package com.android.systemui.power
+
+data class Estimate(val estimateMillis: Long, val isBasedOnUsage: Boolean)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index fdb0b36..41bcab5 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -134,10 +134,6 @@
     private int mShowing;
 
     private long mWarningTriggerTimeMs;
-
-    private Estimate mEstimate;
-    private long mLowWarningThreshold;
-    private long mSevereWarningThreshold;
     private boolean mWarning;
     private boolean mShowAutoSaverSuggestion;
     private boolean mPlaySound;
@@ -148,6 +144,7 @@
     private SystemUIDialog mHighTempDialog;
     private SystemUIDialog mThermalShutdownDialog;
     @VisibleForTesting SystemUIDialog mUsbHighTempDialog;
+    private BatteryStateSnapshot mCurrentBatterySnapshot;
 
     /**
      */
@@ -195,17 +192,8 @@
     }
 
     @Override
-    public void updateEstimate(Estimate estimate) {
-        mEstimate = estimate;
-        if (estimate.estimateMillis <= mLowWarningThreshold) {
-            mWarningTriggerTimeMs = System.currentTimeMillis();
-        }
-    }
-
-    @Override
-    public void updateThresholds(long lowThreshold, long severeThreshold) {
-        mLowWarningThreshold = lowThreshold;
-        mSevereWarningThreshold = severeThreshold;
+    public void updateSnapshot(BatteryStateSnapshot snapshot) {
+        mCurrentBatterySnapshot = snapshot;
     }
 
     private void updateNotification() {
@@ -254,15 +242,17 @@
 
     protected void showWarningNotification() {
         final String percentage = NumberFormat.getPercentInstance()
-                .format((double) mBatteryLevel / 100.0);
+                .format((double) mCurrentBatterySnapshot.getBatteryLevel() / 100.0);
 
-        // get standard notification copy
+        // get shared standard notification copy
         String title = mContext.getString(R.string.battery_low_title);
-        String contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
+        String contentText;
 
-        // override notification copy if hybrid notification enabled
-        if (mEstimate != null) {
+        // get correct content text if notification is hybrid or not
+        if (mCurrentBatterySnapshot.isHybrid()) {
             contentText = getHybridContentString(percentage);
+        } else {
+            contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
         }
 
         final Notification.Builder nb =
@@ -282,8 +272,9 @@
         }
         // Make the notification red if the percentage goes below a certain amount or the time
         // remaining estimate is disabled
-        if (mEstimate == null || mBucket < 0
-                || mEstimate.estimateMillis < mSevereWarningThreshold) {
+        if (!mCurrentBatterySnapshot.isHybrid() || mBucket < 0
+                || mCurrentBatterySnapshot.getTimeRemainingMillis()
+                        < mCurrentBatterySnapshot.getSevereThresholdMillis()) {
             nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
         }
 
@@ -324,10 +315,10 @@
 
     private String getHybridContentString(String percentage) {
         return PowerUtil.getBatteryRemainingStringFormatted(
-            mContext,
-            mEstimate.estimateMillis,
-            percentage,
-            mEstimate.isBasedOnUsage);
+                mContext,
+                mCurrentBatterySnapshot.getTimeRemainingMillis(),
+                percentage,
+                mCurrentBatterySnapshot.isBasedOnUsage());
     }
 
     private PendingIntent pendingBroadcast(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index e27c25e..1863860 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -55,6 +55,7 @@
 import java.util.concurrent.Future;
 
 public class PowerUI extends SystemUI {
+
     static final String TAG = "PowerUI";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
@@ -63,6 +64,7 @@
     static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
     private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
     private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
+    public static final int NO_ESTIMATE_AVAILABLE = -1;
 
     private final Handler mHandler = new Handler();
     @VisibleForTesting
@@ -71,13 +73,9 @@
     private PowerManager mPowerManager;
     private WarningsUI mWarnings;
     private final Configuration mLastConfiguration = new Configuration();
-    private long mTimeRemaining = Long.MAX_VALUE;
     private int mPlugType = 0;
     private int mInvalidCharger = 0;
     private EnhancedEstimates mEnhancedEstimates;
-    private Estimate mLastEstimate;
-    private boolean mLowWarningShownThisChargeCycle;
-    private boolean mSevereWarningShownThisChargeCycle;
     private Future mLastShowWarningTask;
     private boolean mEnableSkinTemperatureWarning;
     private boolean mEnableUsbTemperatureAlarm;
@@ -87,6 +85,10 @@
 
     private long mScreenOffTime = -1;
 
+    @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
+    @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
+    @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
+    @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
     @VisibleForTesting IThermalService mThermalService;
 
     @VisibleForTesting int mBatteryLevel = 100;
@@ -205,6 +207,7 @@
                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
                 final int oldInvalidCharger = mInvalidCharger;
                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
+                mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
 
                 final boolean plugged = mPlugType != 0;
                 final boolean oldPlugged = oldPlugType != 0;
@@ -233,16 +236,22 @@
                     mWarnings.dismissInvalidChargerWarning();
                 } else if (mWarnings.isInvalidChargerWarningShowing()) {
                     // if invalid charger is showing, don't show low battery
+                    if (DEBUG) {
+                        Slog.d(TAG, "Bad Charger");
+                    }
                     return;
                 }
 
                 // Show the correct version of low battery warning if needed
                 if (mLastShowWarningTask != null) {
                     mLastShowWarningTask.cancel(true);
+                    if (DEBUG) {
+                        Slog.d(TAG, "cancelled task");
+                    }
                 }
                 mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
-                    maybeShowBatteryWarning(
-                            oldBatteryLevel, plugged, oldPlugged, oldBucket, bucket);
+                    maybeShowBatteryWarningV2(
+                            plugged, bucket);
                 });
 
             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
@@ -257,53 +266,150 @@
         }
     }
 
-    protected void maybeShowBatteryWarning(int oldBatteryLevel, boolean plugged, boolean oldPlugged,
-            int oldBucket, int bucket) {
-        boolean isPowerSaver = mPowerManager.isPowerSaveMode();
-        // only play SFX when the dialog comes up or the bucket changes
-        final boolean playSound = bucket != oldBucket || oldPlugged;
+    protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
         final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
-        if (hybridEnabled) {
-            Estimate estimate = mLastEstimate;
-            if (estimate == null || mBatteryLevel != oldBatteryLevel) {
-                estimate = mEnhancedEstimates.getEstimate();
-                mLastEstimate = estimate;
-            }
-            // Turbo is not always booted once SysUI is running so we have to make sure we actually
-            // get data back
-            if (estimate != null) {
-                mTimeRemaining = estimate.estimateMillis;
-                mWarnings.updateEstimate(estimate);
-                mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(),
-                        mEnhancedEstimates.getSevereWarningThreshold());
+        final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
 
-                // if we are now over 45% battery & 6 hours remaining we can trigger hybrid
-                // notification again
-                if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET
-                        && mTimeRemaining > SIX_HOURS_MILLIS) {
-                    mLowWarningShownThisChargeCycle = false;
-                    mSevereWarningShownThisChargeCycle = false;
-                }
+        // Stick current battery state into an immutable container to determine if we should show
+        // a warning.
+        if (DEBUG) {
+            Slog.d(TAG, "evaluating which notification to show");
+        }
+        if (hybridEnabled) {
+            if (DEBUG) {
+                Slog.d(TAG, "using hybrid");
+            }
+            Estimate estimate = refreshEstimateIfNeeded();
+            mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
+                    plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
+                    mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
+                    mEnhancedEstimates.getSevereWarningThreshold(),
+                    mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage());
+        } else {
+            if (DEBUG) {
+                Slog.d(TAG, "using standard");
+            }
+            mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
+                    plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
+                    mLowBatteryReminderLevels[0]);
+        }
+
+        mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
+        if (mCurrentBatteryStateSnapshot.isHybrid()) {
+            maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
+        } else {
+            maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
+        }
+    }
+
+    // updates the time estimate if we don't have one or battery level has changed.
+    @VisibleForTesting
+    Estimate refreshEstimateIfNeeded() {
+        if (mLastBatteryStateSnapshot == null
+                || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
+                || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
+            final Estimate estimate = mEnhancedEstimates.getEstimate();
+            if (DEBUG) {
+                Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
+            }
+            return estimate;
+        }
+        return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
+                mLastBatteryStateSnapshot.isBasedOnUsage());
+    }
+
+    @VisibleForTesting
+    void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
+            BatteryStateSnapshot lastSnapshot) {
+        // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
+        // notification again
+        if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
+                && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
+            mLowWarningShownThisChargeCycle = false;
+            mSevereWarningShownThisChargeCycle = false;
+            if (DEBUG) {
+                Slog.d(TAG, "Charge cycle reset! Can show warnings again");
             }
         }
 
-        if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket,
-                mTimeRemaining, isPowerSaver, mBatteryStatus)) {
-            mWarnings.showLowBatteryWarning(playSound);
+        final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
+                || lastSnapshot.getPlugged();
 
+        if (shouldShowHybridWarning(currentSnapshot)) {
+            mWarnings.showLowBatteryWarning(playSound);
             // mark if we've already shown a warning this cycle. This will prevent the notification
             // trigger from spamming users by only showing low/critical warnings once per cycle
-            if (hybridEnabled) {
-                if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold()
-                        || mBatteryLevel <= mLowBatteryReminderLevels[1]) {
-                    mSevereWarningShownThisChargeCycle = true;
-                    mLowWarningShownThisChargeCycle = true;
-                } else {
-                    mLowWarningShownThisChargeCycle = true;
+            if (currentSnapshot.getTimeRemainingMillis()
+                    <= currentSnapshot.getSevereLevelThreshold()
+                    || currentSnapshot.getBatteryLevel() <= mLowBatteryReminderLevels[1]) {
+                mSevereWarningShownThisChargeCycle = true;
+                mLowWarningShownThisChargeCycle = true;
+                if (DEBUG) {
+                    Slog.d(TAG, "Severe warning marked as shown this cycle");
                 }
+            } else {
+                Slog.d(TAG, "Low warning marked as shown this cycle");
+                mLowWarningShownThisChargeCycle = true;
             }
-        } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
-                isPowerSaver)) {
+
+        } else if (shouldDismissHybridWarning(currentSnapshot)) {
+            if (DEBUG) {
+                Slog.d(TAG, "Dismissing warning");
+            }
+            mWarnings.dismissLowBatteryWarning();
+        } else {
+            if (DEBUG) {
+                Slog.d(TAG, "Updating warning");
+            }
+            mWarnings.updateLowBatteryWarning();
+        }
+    }
+
+    @VisibleForTesting
+    boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
+        if (snapshot.getPlugged()
+                || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
+            Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
+                    + " status unknown: "
+                    + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
+            return false;
+        }
+
+        // Only show the low warning once per charge cycle & no battery saver
+        final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
+                && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
+                || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
+
+        // Only show the severe warning once per charge cycle
+        final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
+                && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
+                || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
+
+        final boolean canShow = canShowWarning || canShowSevereWarning;
+        if (DEBUG) {
+            Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
+                    + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
+                    + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
+                    + "\n" + snapshot.toString());
+        }
+        return canShow;
+    }
+
+    @VisibleForTesting
+    boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
+        return snapshot.getPlugged()
+                || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
+    }
+
+    protected void maybeShowBatteryWarning(
+            BatteryStateSnapshot currentSnapshot,
+            BatteryStateSnapshot lastSnapshot) {
+        final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
+                || lastSnapshot.getPlugged();
+
+        if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
+            mWarnings.showLowBatteryWarning(playSound);
+        } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
             mWarnings.dismissLowBatteryWarning();
         } else {
             mWarnings.updateLowBatteryWarning();
@@ -311,64 +417,25 @@
     }
 
     @VisibleForTesting
-    boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
-            int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) {
-        if (mEnhancedEstimates.isHybridNotificationEnabled()) {
-            // triggering logic when enhanced estimate is available
-            return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus);
-        }
-        // legacy triggering logic
-        return !plugged
-                && !isPowerSaver
-                && (((bucket < oldBucket || oldPlugged) && bucket < 0))
-                && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
+    boolean shouldShowLowBatteryWarning(
+            BatteryStateSnapshot currentSnapshot,
+            BatteryStateSnapshot lastSnapshot) {
+        return !currentSnapshot.getPlugged()
+                && !currentSnapshot.isPowerSaver()
+                && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
+                        || lastSnapshot.getPlugged())
+                && currentSnapshot.getBucket() < 0))
+                && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
     }
 
     @VisibleForTesting
-    boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
-            long timeRemaining, boolean isPowerSaver) {
-        final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
-        final boolean hybridWouldDismiss = hybridEnabled
-                && timeRemaining > mEnhancedEstimates.getLowWarningThreshold();
-        final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
-        return (isPowerSaver && !hybridEnabled)
-                || plugged
-                || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
-                        || hybridWouldDismiss));
-    }
-
-    private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
-            int batteryStatus) {
-        if (plugged || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
-            return false;
-        }
-        int warnLevel = mLowBatteryReminderLevels[0];
-        int critLevel = mLowBatteryReminderLevels[1];
-
-        // Only show the low warning once per charge cycle & no battery saver
-        final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !isPowerSaver
-                && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
-                || mBatteryLevel <= warnLevel);
-
-        // Only show the severe warning once per charge cycle
-        final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
-                && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
-                || mBatteryLevel <= critLevel);
-
-        final boolean canShow = canShowWarning || canShowSevereWarning;
-        if (DEBUG) {
-            Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith values: "
-                    + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
-                    + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
-                    + " mEnhancedEstimates.timeremaining: " + timeRemaining
-                    + " mBatteryLevel: " + mBatteryLevel
-                    + " canShowWarning: " + canShowWarning
-                    + " canShowSevereWarning: " + canShowSevereWarning
-                    + " plugged: " + plugged
-                    + " batteryStatus: " + batteryStatus
-                    + " isPowerSaver: " + isPowerSaver);
-        }
-        return canShowWarning || canShowSevereWarning;
+    boolean shouldDismissLowBatteryWarning(
+            BatteryStateSnapshot currentSnapshot,
+            BatteryStateSnapshot lastSnapshot) {
+        return currentSnapshot.isPowerSaver()
+                || currentSnapshot.getPlugged()
+                || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
+                        && currentSnapshot.getBucket() > 0);
     }
 
     private void initTemperature() {
@@ -453,13 +520,21 @@
         mWarnings.dump(pw);
     }
 
+    /**
+     * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
+     * is being used by the system.
+     */
     public interface WarningsUI {
+
+        /**
+         * Updates battery and screen info for determining whether to trigger battery warnings or
+         * not.
+         * @param batteryLevel The current battery level
+         * @param bucket The current battery bucket
+         * @param screenOffTime How long the screen has been off in millis
+         */
         void update(int batteryLevel, int bucket, long screenOffTime);
 
-        void updateEstimate(Estimate estimate);
-
-        void updateThresholds(long lowThreshold, long severeThreshold);
-
         void dismissLowBatteryWarning();
 
         void showLowBatteryWarning(boolean playSound);
@@ -486,6 +561,12 @@
         void dump(PrintWriter pw);
 
         void userSwitched();
+
+        /**
+         * Updates the snapshot of battery state used for evaluating battery warnings
+         * @param snapshot object containing relevant values for making battery warning decisions.
+         */
+        void updateSnapshot(BatteryStateSnapshot snapshot);
     }
 
     // Thermal event received from thermal service manager subsystem
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a354885..142f398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -20,9 +20,6 @@
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
-
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -2956,6 +2953,9 @@
         if (mKeyguardStatusBar != null) {
             mKeyguardStatusBar.dump(fd, pw, args);
         }
+        if (mKeyguardStatusView != null) {
+            mKeyguardStatusView.dump(fd, pw, args);
+        }
     }
 
     public boolean hasActiveClearableNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index af3c96f..3fa3e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -226,7 +226,7 @@
 
         String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
         return PowerUtil.getBatteryRemainingShortStringFormatted(
-                mContext, mEstimate.estimateMillis);
+                mContext, mEstimate.getEstimateMillis());
     }
 
     private void updateEstimateInBackground() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
index c6d6218..7edb5a5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Util.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -16,52 +16,13 @@
 
 package com.android.systemui.volume;
 
-import android.content.Context;
 import android.media.AudioManager;
-import android.media.MediaMetadata;
-import android.media.VolumeProvider;
-import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.PlaybackState;
-import android.telephony.TelephonyManager;
 import android.view.View;
-import android.widget.TextView;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Objects;
 
 /**
  * Static helpers for the volume dialog.
  */
-class Util {
-
-    // Note: currently not shown (only used in the text footer)
-    private static final SimpleDateFormat HMMAA = new SimpleDateFormat("h:mm aa", Locale.US);
-
-    private static int[] AUDIO_MANAGER_FLAGS = new int[] {
-        AudioManager.FLAG_SHOW_UI,
-        AudioManager.FLAG_VIBRATE,
-        AudioManager.FLAG_PLAY_SOUND,
-        AudioManager.FLAG_ALLOW_RINGER_MODES,
-        AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
-        AudioManager.FLAG_SHOW_VIBRATE_HINT,
-        AudioManager.FLAG_SHOW_SILENT_HINT,
-        AudioManager.FLAG_FROM_KEY,
-        AudioManager.FLAG_SHOW_UI_WARNINGS,
-    };
-
-    private static String[] AUDIO_MANAGER_FLAG_NAMES = new String[] {
-        "SHOW_UI",
-        "VIBRATE",
-        "PLAY_SOUND",
-        "ALLOW_RINGER_MODES",
-        "REMOVE_SOUND_AND_VIBRATE",
-        "SHOW_VIBRATE_HINT",
-        "SHOW_SILENT_HINT",
-        "FROM_KEY",
-        "SHOW_UI_WARNINGS",
-    };
+class Util extends com.android.settingslib.volume.Util {
 
     public static String logTag(Class<?> c) {
         final String tag = "vol." + c.getSimpleName();
@@ -70,106 +31,19 @@
 
     public static String ringerModeToString(int ringerMode) {
         switch (ringerMode) {
-            case AudioManager.RINGER_MODE_SILENT: return "RINGER_MODE_SILENT";
-            case AudioManager.RINGER_MODE_VIBRATE: return "RINGER_MODE_VIBRATE";
-            case AudioManager.RINGER_MODE_NORMAL: return "RINGER_MODE_NORMAL";
-            default: return "RINGER_MODE_UNKNOWN_" + ringerMode;
+            case AudioManager.RINGER_MODE_SILENT:
+                return "RINGER_MODE_SILENT";
+            case AudioManager.RINGER_MODE_VIBRATE:
+                return "RINGER_MODE_VIBRATE";
+            case AudioManager.RINGER_MODE_NORMAL:
+                return "RINGER_MODE_NORMAL";
+            default:
+                return "RINGER_MODE_UNKNOWN_" + ringerMode;
         }
     }
 
-    public static String mediaMetadataToString(MediaMetadata metadata) {
-        if (metadata == null) return null;
-        return metadata.getDescription().toString();
-    }
-
-    public static String playbackInfoToString(PlaybackInfo info) {
-        if (info == null) return null;
-        final String type = playbackInfoTypeToString(info.getPlaybackType());
-        final String vc = volumeProviderControlToString(info.getVolumeControl());
-        return String.format("PlaybackInfo[vol=%s,max=%s,type=%s,vc=%s],atts=%s",
-                info.getCurrentVolume(), info.getMaxVolume(), type, vc, info.getAudioAttributes());
-    }
-
-    public static String playbackInfoTypeToString(int type) {
-        switch (type) {
-            case PlaybackInfo.PLAYBACK_TYPE_LOCAL: return "LOCAL";
-            case PlaybackInfo.PLAYBACK_TYPE_REMOTE: return "REMOTE";
-            default: return "UNKNOWN_" + type;
-        }
-    }
-
-    public static String playbackStateStateToString(int state) {
-        switch (state) {
-            case PlaybackState.STATE_NONE: return "STATE_NONE";
-            case PlaybackState.STATE_STOPPED: return "STATE_STOPPED";
-            case PlaybackState.STATE_PAUSED: return "STATE_PAUSED";
-            case PlaybackState.STATE_PLAYING: return "STATE_PLAYING";
-            default: return "UNKNOWN_" + state;
-        }
-    }
-
-    public static String volumeProviderControlToString(int control) {
-        switch (control) {
-            case VolumeProvider.VOLUME_CONTROL_ABSOLUTE: return "VOLUME_CONTROL_ABSOLUTE";
-            case VolumeProvider.VOLUME_CONTROL_FIXED: return "VOLUME_CONTROL_FIXED";
-            case VolumeProvider.VOLUME_CONTROL_RELATIVE: return "VOLUME_CONTROL_RELATIVE";
-            default: return "VOLUME_CONTROL_UNKNOWN_" + control;
-        }
-    }
-
-    public static String playbackStateToString(PlaybackState playbackState) {
-        if (playbackState == null) return null;
-        return playbackStateStateToString(playbackState.getState()) + " " + playbackState;
-    }
-
-    public static String audioManagerFlagsToString(int value) {
-        return bitFieldToString(value, AUDIO_MANAGER_FLAGS, AUDIO_MANAGER_FLAG_NAMES);
-    }
-
-    private static String bitFieldToString(int value, int[] values, String[] names) {
-        if (value == 0) return "";
-        final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if ((value & values[i]) != 0) {
-                if (sb.length() > 0) sb.append(',');
-                sb.append(names[i]);
-            }
-            value &= ~values[i];
-        }
-        if (value != 0) {
-            if (sb.length() > 0) sb.append(',');
-            sb.append("UNKNOWN_").append(value);
-        }
-        return sb.toString();
-    }
-
-    public static String getShortTime(long millis) {
-        return HMMAA.format(new Date(millis));
-    }
-
-    private static CharSequence emptyToNull(CharSequence str) {
-        return str == null || str.length() == 0 ? null : str;
-    }
-
-    public static boolean setText(TextView tv, CharSequence text) {
-        if (Objects.equals(emptyToNull(tv.getText()), emptyToNull(text))) return false;
-        tv.setText(text);
-        return true;
-    }
-
     public static final void setVisOrGone(View v, boolean vis) {
         if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
         v.setVisibility(vis ? View.VISIBLE : View.GONE);
     }
-
-    public static final void setVisOrInvis(View v, boolean vis) {
-        if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
-        v.setVisibility(vis ? View.VISIBLE : View.INVISIBLE);
-    }
-
-    public static boolean isVoiceCapable(Context context) {
-        final TelephonyManager telephony =
-                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        return telephony != null && telephony.isVoiceCapable();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 32bc01c..4c16297 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -53,6 +53,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.settingslib.volume.MediaSessions;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 5876ae1..58c9311 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -30,6 +30,7 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.os.BatteryManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -57,6 +58,9 @@
         // Test Instance.
         mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
         mPowerNotificationWarnings = new PowerNotificationWarnings(mContext);
+        BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
+                BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
+        mPowerNotificationWarnings.updateSnapshot(snapshot);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 0aed63d..f51e473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -17,8 +17,7 @@
 import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING;
 import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.anyObject;
@@ -29,7 +28,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.Intent;
 import android.os.BatteryManager;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
@@ -42,22 +40,20 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
 
-import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.power.PowerUI.WarningsUI;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.time.Duration;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
@@ -75,6 +71,7 @@
     private static final int OLD_BATTERY_LEVEL_NINE = 9;
     private static final int OLD_BATTERY_LEVEL_10 = 10;
     private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15);
+    public static final int BATTERY_LEVEL_10 = 10;
     private WarningsUI mMockWarnings;
     private PowerUI mPowerUI;
     private EnhancedEstimates mEnhancedEstimates;
@@ -176,368 +173,333 @@
     }
 
     @Test
-    public void testShouldShowLowBatteryWarning_showHybridOnly_overrideThresholdHigh_returnsNoShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold())
-                .thenReturn(Duration.ofHours(1).toMillis());
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+    public void testMaybeShowHybridWarning() {
         mPowerUI.start();
 
-        // unplugged device that would not show the non-hybrid notification but would show the
-        // hybrid but the threshold has been overriden to be too low
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertFalse(shouldShow);
+        // verify low warning shown this cycle noticed
+        BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
+        BatteryStateSnapshot lastState = state.get();
+        state.mTimeRemainingMillis = Duration.ofHours(2).toMillis();
+        state.mBatteryLevel = 15;
+
+        mPowerUI.maybeShowHybridWarning(state.get(), lastState);
+
+        assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isTrue();
+        assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isFalse();
+
+        // verify severe warning noticed this cycle
+        lastState = state.get();
+        state.mBatteryLevel = 1;
+        state.mTimeRemainingMillis = Duration.ofMinutes(10).toMillis();
+
+        mPowerUI.maybeShowHybridWarning(state.get(), lastState);
+
+        assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isTrue();
+        assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isTrue();
+
+        // verify getting past threshold resets values
+        lastState = state.get();
+        state.mBatteryLevel = 100;
+        state.mTimeRemainingMillis = Duration.ofDays(1).toMillis();
+
+        mPowerUI.maybeShowHybridWarning(state.get(), lastState);
+
+        assertThat(mPowerUI.mLowWarningShownThisChargeCycle).isFalse();
+        assertThat(mPowerUI.mSevereWarningShownThisChargeCycle).isFalse();
     }
 
     @Test
-    public void testShouldShowLowBatteryWarning_showHybridOnly_overrideThresholdHigh_returnsShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold())
-                .thenReturn(Duration.ofHours(5).toMillis());
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+    public void testShouldShowHybridWarning_lowLevelWarning() {
         mPowerUI.start();
+        mPowerUI.mLowWarningShownThisChargeCycle = false;
+        mPowerUI.mSevereWarningShownThisChargeCycle = false;
+        BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
 
-        // unplugged device that would not show the non-hybrid notification but would show the
-        // hybrid since the threshold has been overriden to be much higher
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertTrue(shouldShow);
+        // sanity check to make sure we can show for a valid config
+        state.mBatteryLevel = 10;
+        state.mTimeRemainingMillis = Duration.ofHours(2).toMillis();
+        boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // Shouldn't show if plugged in
+        state.mPlugged = true;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        // Shouldn't show if battery is unknown
+        state.mPlugged = false;
+        state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+        // Already shown both warnings
+        mPowerUI.mLowWarningShownThisChargeCycle = true;
+        mPowerUI.mSevereWarningShownThisChargeCycle = true;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        // Can show low warning
+        mPowerUI.mLowWarningShownThisChargeCycle = false;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // Can't show if above the threshold for time & battery
+        state.mTimeRemainingMillis = Duration.ofHours(1000).toMillis();
+        state.mBatteryLevel = 100;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        // Battery under low percentage threshold but not time
+        state.mBatteryLevel = 10;
+        state.mLowLevelThreshold = 50;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // Should also trigger if both level and time remaining under low threshold
+        state.mTimeRemainingMillis = Duration.ofHours(2).toMillis();
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // battery saver should block the low level warning though
+        state.mIsPowerSaver = true;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
     }
 
     @Test
-    public void testShouldShowLowBatteryWarning_showHybridOnly_returnsShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+    public void testShouldShowHybridWarning_severeLevelWarning() {
         mPowerUI.start();
+        mPowerUI.mLowWarningShownThisChargeCycle = false;
+        mPowerUI.mSevereWarningShownThisChargeCycle = false;
+        BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
 
-        // unplugged device that would not show the non-hybrid notification but would show the
-        // hybrid
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertTrue(shouldShow);
+        // sanity check to make sure we can show for a valid config
+        state.mBatteryLevel = 1;
+        state.mTimeRemainingMillis = Duration.ofMinutes(1).toMillis();
+        boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // Shouldn't show if plugged in
+        state.mPlugged = true;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        // Shouldn't show if battery is unknown
+        state.mPlugged = false;
+        state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+        // Already shown both warnings
+        mPowerUI.mLowWarningShownThisChargeCycle = true;
+        mPowerUI.mSevereWarningShownThisChargeCycle = true;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        // Can show severe warning
+        mPowerUI.mSevereWarningShownThisChargeCycle = false;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // Can't show if above the threshold for time & battery
+        state.mTimeRemainingMillis = Duration.ofHours(1000).toMillis();
+        state.mBatteryLevel = 100;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isFalse();
+
+        // Battery under low percentage threshold but not time
+        state.mBatteryLevel = 1;
+        state.mSevereLevelThreshold = 5;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // Should also trigger if both level and time remaining under low threshold
+        state.mTimeRemainingMillis = Duration.ofHours(2).toMillis();
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
+
+        // battery saver should not block the severe level warning though
+        state.mIsPowerSaver = true;
+        shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
+        assertThat(shouldShow).isTrue();
     }
 
     @Test
-    public void testShouldShowLowBatteryWarning_showHybrid_showStandard_returnsShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        mPowerUI.mBatteryLevel = 10;
+    public void testShouldDismissHybridWarning() {
         mPowerUI.start();
+        BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
 
-        // unplugged device that would show the non-hybrid notification and the hybrid
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertTrue(shouldShow);
+        // We should dismiss if the device is plugged in
+        state.mPlugged = true;
+        state.mTimeRemainingMillis = Duration.ofHours(1).toMillis();
+        state.mLowThresholdMillis = Duration.ofHours(2).toMillis();
+        boolean shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get());
+        assertThat(shouldDismiss).isTrue();
+
+        // If not plugged in and below the threshold we should not dismiss
+        state.mPlugged = false;
+        shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get());
+        assertThat(shouldDismiss).isFalse();
+
+        // If we go over the low warning threshold we should dismiss
+        state.mTimeRemainingMillis = Duration.ofHours(3).toMillis();
+        shouldDismiss = mPowerUI.shouldDismissHybridWarning(state.get());
+        assertThat(shouldDismiss).isTrue();
     }
 
     @Test
-    public void testShouldShowLowBatteryWarning_showStandardOnly_returnsShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        mPowerUI.mBatteryLevel = 10;
-        mPowerUI.start();
-
-        // unplugged device that would show the non-hybrid but not the hybrid
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertTrue(shouldShow);
-    }
-
-    @Test
-    public void testShouldShowLowBatteryWarning_deviceHighBattery_returnsNoShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        mPowerUI.start();
-
-        // unplugged device that would show the neither due to battery level being good
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertFalse(shouldShow);
-    }
-
-    @Test
-    public void testShouldShowLowBatteryWarning_devicePlugged_returnsNoShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        mPowerUI.start();
-
-        // plugged device that would show the neither due to being plugged
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(!UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertFalse(shouldShow);
-   }
-
-    @Test
-    public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnknown_returnsNoShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        mPowerUI.start();
-
-        // Unknown battery status device that would show the neither due to the battery status being
-        // unknown
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        !POWER_SAVER_OFF, BatteryManager.BATTERY_STATUS_UNKNOWN);
-        assertFalse(shouldShow);
-    }
-
-    @Test
-    public void testShouldShowLowBatteryWarning_batterySaverEnabled_returnsNoShow() {
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        mPowerUI.start();
-
-        // BatterySaverEnabled device that would show the neither due to battery saver
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        !POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertFalse(shouldShow);
-    }
-
-    @Test
-    public void testShouldShowLowBatteryWarning_onlyShowsOncePerChargeCycle() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        when(mEnhancedEstimates.getEstimate())
-                .thenReturn(new Estimate(BELOW_HYBRID_THRESHOLD, true));
-        mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
-
-        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
-                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
-
-        // reduce battery level to handle time based trigger -> level trigger interactions
-        mPowerUI.mBatteryLevel = 10;
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertFalse(shouldShow);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabledLegacy() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(false);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // device that gets power saver turned on should dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, !POWER_SAVER_OFF);
-        assertTrue(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldNotDismissLowBatteryWarning_dismissWhenPowerSaverEnabledHybrid() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // device that gets power saver turned on should dismiss
-        boolean shouldDismiss =
-            mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, !POWER_SAVER_OFF);
-        assertFalse(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_dismissWhenPlugged() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // device that gets plugged in should dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(!UNPLUGGED, BELOW_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
-        assertTrue(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_dismissHybridSignal_showStandardSignal_shouldShow() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // would dismiss hybrid but not non-hybrid should not dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
-        assertFalse(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_showHybridSignal_dismissStandardSignal_shouldShow() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // would dismiss non-hybrid but not hybrid should not dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, POWER_SAVER_OFF);
-        assertFalse(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_showBothSignal_shouldShow() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // should not dismiss when both would not dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, POWER_SAVER_OFF);
-        assertFalse(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_dismissBothSignal_shouldDismiss() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        //should dismiss if both would dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
-        assertTrue(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_dismissStandardSignal_hybridDisabled_shouldDismiss() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(false);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-
-        // would dismiss non-hybrid with hybrid disabled should dismiss
-        boolean shouldDismiss =
-                mPowerUI.shouldDismissLowBatteryWarning(UNPLUGGED, BELOW_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, POWER_SAVER_OFF);
-        assertTrue(shouldDismiss);
-    }
-
-    @Test
-    public void testShouldDismissLowBatteryWarning_powerSaverModeEnabled()
-            throws InterruptedException {
-        when(mPowerManager.isPowerSaveMode()).thenReturn(true);
-
-        mPowerUI.start();
-        mPowerUI.mReceiver.onReceive(mContext,
-                new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
-
-        CountDownLatch latch = new CountDownLatch(1);
-        ThreadUtils.postOnBackgroundThread(() -> latch.countDown());
-        latch.await(5, TimeUnit.SECONDS);
-
-        verify(mMockWarnings).dismissLowBatteryWarning();
-    }
-
-    @Test
-    public void testShouldNotDismissLowBatteryWarning_powerSaverModeDisabled()
-            throws InterruptedException {
-        when(mPowerManager.isPowerSaveMode()).thenReturn(false);
-
-        mPowerUI.start();
-        mPowerUI.mReceiver.onReceive(mContext,
-                new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
-
-        CountDownLatch latch = new CountDownLatch(1);
-        ThreadUtils.postOnBackgroundThread(() -> latch.countDown());
-        latch.await(5, TimeUnit.SECONDS);
-
-        verify(mMockWarnings, never()).dismissLowBatteryWarning();
-    }
-
-    @Test
-    public void testSevereWarning_countsAsLowAndSevere_WarningOnlyShownOnce() {
-        mPowerUI.start();
-        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
-        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
-        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
-        when(mEnhancedEstimates.getEstimate())
-                .thenReturn(new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true));
-        mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
-
-        // reduce battery level to handle time based trigger -> level trigger interactions
-        mPowerUI.mBatteryLevel = 5;
-        boolean shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, BELOW_SEVERE_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertTrue(shouldShow);
-
-        // actually run the end to end since it handles changing the internal state.
-        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
-                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
-
-        shouldShow =
-                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
-                        ABOVE_WARNING_BUCKET, VERY_BELOW_SEVERE_HYBRID_THRESHOLD,
-                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
-        assertFalse(shouldShow);
-    }
-
-    @Test
-    public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
+    public void testRefreshEstimateIfNeeded_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
         mPowerUI.start();
         Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
         when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
         when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
         when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
         when(mEnhancedEstimates.getEstimate()).thenReturn(estimate);
-        mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+        mPowerUI.mBatteryLevel = 10;
 
-        // we expect that the first time it will query even if the level is the same
+        // we expect that the first time it will query since there is no last battery snapshot.
+        // However an invalid estimate (-1) is returned.
+        Estimate refreshedEstimate = mPowerUI.refreshEstimateIfNeeded();
+        assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD);
+        BatteryStateSnapshot snapshot = new BatteryStateSnapshot(
+                BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD,
+                0, 0, -1, 0, 0, false);
+        mPowerUI.mLastBatteryStateSnapshot = snapshot;
+
+        // query again since the estimate was -1
+        estimate = new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true);
+        when(mEnhancedEstimates.getEstimate()).thenReturn(estimate);
+        refreshedEstimate = mPowerUI.refreshEstimateIfNeeded();
+        assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD);
+        snapshot = new BatteryStateSnapshot(
+                BATTERY_LEVEL_10, false, false, 0, BatteryManager.BATTERY_HEALTH_GOOD, 0,
+                0, BELOW_SEVERE_HYBRID_THRESHOLD, 0, 0, false);
+        mPowerUI.mLastBatteryStateSnapshot = snapshot;
+
+        // Battery level hasn't changed, so we don't query again
+        estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
+        when(mEnhancedEstimates.getEstimate()).thenReturn(estimate);
+        refreshedEstimate = mPowerUI.refreshEstimateIfNeeded();
+        assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_SEVERE_HYBRID_THRESHOLD);
+
+        // Battery level changes so we update again
         mPowerUI.mBatteryLevel = 9;
-        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
-                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
-        verify(mEnhancedEstimates, times(1)).getEstimate();
+        refreshedEstimate = mPowerUI.refreshEstimateIfNeeded();
+        assertThat(refreshedEstimate.getEstimateMillis()).isEqualTo(BELOW_HYBRID_THRESHOLD);
+    }
 
-        // We should NOT query again if the battery level hasn't changed
-        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
-                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
-        verify(mEnhancedEstimates, times(1)).getEstimate();
+    @Test
+    public void testShouldShowStandardWarning() {
+        mPowerUI.start();
+        BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
+        state.mIsHybrid = false;
+        BatteryStateSnapshot lastState = state.get();
 
-        // Battery level has changed, so we should query again
-        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
-                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
-        verify(mEnhancedEstimates, times(2)).getEstimate();
+        // sanity check to make sure we can show for a valid config
+        state.mBatteryLevel = 10;
+        state.mBucket = -1;
+        boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isTrue();
+        lastState = state.get();
+
+        // Shouldn't show if plugged in
+        state.mPlugged = true;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isFalse();
+
+        state.mPlugged = false;
+        // Shouldn't show if battery saver
+        state.mIsPowerSaver = true;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isFalse();
+
+        state.mIsPowerSaver = false;
+        // Shouldn't show if battery is unknown
+        state.mPlugged = false;
+        state.mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isFalse();
+
+        state.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+        // show if plugged -> unplugged, bucket -1 -> -1
+        state.mPlugged = true;
+        state.mBucket = -1;
+        lastState = state.get();
+        state.mPlugged = false;
+        state.mBucket = -1;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isTrue();
+
+        // don't show if plugged -> unplugged, bucket 0 -> 0
+        state.mPlugged = true;
+        state.mBucket = 0;
+        lastState = state.get();
+        state.mPlugged = false;
+        state.mBucket = 0;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isFalse();
+
+        // show if unplugged -> unplugged, bucket 0 -> -1
+        state.mPlugged = false;
+        state.mBucket = 0;
+        lastState = state.get();
+        state.mPlugged = false;
+        state.mBucket = -1;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isTrue();
+
+        // don't show if unplugged -> unplugged, bucket -1 -> 1
+        state.mPlugged = false;
+        state.mBucket = -1;
+        lastState = state.get();
+        state.mPlugged = false;
+        state.mBucket = 1;
+        shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldShow).isFalse();
+    }
+
+    @Test
+    public void testShouldDismissStandardWarning() {
+        mPowerUI.start();
+        BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper();
+        state.mIsHybrid = false;
+        BatteryStateSnapshot lastState = state.get();
+
+        // should dismiss if battery saver
+        state.mIsPowerSaver = true;
+        boolean shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldDismiss).isTrue();
+
+        state.mIsPowerSaver = false;
+        // should dismiss if plugged
+        state.mPlugged = true;
+        shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldDismiss).isTrue();
+
+        state.mPlugged = false;
+        // should dismiss if bucket 0 -> 1
+        state.mBucket = 0;
+        lastState = state.get();
+        state.mBucket = 1;
+        shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldDismiss).isTrue();
+
+        // shouldn't dismiss if bucket -1 -> 0
+        state.mBucket = -1;
+        lastState = state.get();
+        state.mBucket = 0;
+        shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldDismiss).isFalse();
+
+        // should dismiss if powersaver & bucket 0 -> 1
+        state.mIsPowerSaver = true;
+        state.mBucket = 0;
+        lastState = state.get();
+        state.mBucket = 1;
+        shouldDismiss = mPowerUI.shouldDismissLowBatteryWarning(state.get(), lastState);
+        assertThat(shouldDismiss).isTrue();
     }
 
     private Temperature getEmergencyStatusTemp(int type, String name) {
@@ -556,4 +518,35 @@
         mPowerUI.mComponents = mContext.getComponents();
         mPowerUI.mThermalService = mThermalServiceMock;
     }
+
+    /**
+     * A simple wrapper class that sets values by default and makes them not final to improve
+     * test clarity.
+     */
+    private class BatteryStateSnapshotWrapper {
+        public int mBatteryLevel = 100;
+        public boolean mIsPowerSaver = false;
+        public boolean mPlugged = false;
+        public long mSevereThresholdMillis = Duration.ofHours(1).toMillis();
+        public long mLowThresholdMillis = Duration.ofHours(3).toMillis();
+        public int mSevereLevelThreshold = 5;
+        public int mLowLevelThreshold = 15;
+        public int mBucket = 1;
+        public int mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+        public long mTimeRemainingMillis = Duration.ofHours(24).toMillis();
+        public boolean mIsBasedOnUsage = true;
+        public boolean mIsHybrid = true;
+
+        public BatteryStateSnapshot get() {
+            if (mIsHybrid) {
+                return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket,
+                        mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold,
+                        mTimeRemainingMillis, mSevereThresholdMillis, mLowThresholdMillis,
+                        mIsBasedOnUsage);
+            } else {
+                return new BatteryStateSnapshot(mBatteryLevel, mIsPowerSaver, mPlugged, mBucket,
+                        mBatteryStatus, mSevereLevelThreshold, mLowLevelThreshold);
+            }
+        }
+    }
 }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5a639b2..91b161d 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -253,6 +253,13 @@
     MANAGED_PROFILE = 2;
   }
 
+  // Subtypes for showing direct sharing targets
+  enum DirectShareTargetPreviousState {
+    PREVIOUSLY_UNKNOWN = 0;
+    PREVIOUSLY_HIDDEN = 1;
+    PREVIOUSLY_VISIBLE = 2;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -7068,6 +7075,18 @@
     // OPEN: Accessibility detail settings (android.settings.ACCESSIBILITY_DETAILS_SETTINGS intent)
     ACCESSIBILITY_DETAILS_SETTINGS = 1682;
 
+    // Open: Settings will show the conditional when Grayscale mode is on
+    SETTINGS_CONDITION_GRAYSCALE_MODE = 1683;
+
+    // ACTION: Individual contextual card loading time
+    ACTION_CONTEXTUAL_CARD_LOAD = 1684;
+
+    //ACTION: Contextual card loading timeout
+    ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT = 1685;
+
+    //ACTION: Log result for each card's eligibility check
+    ACTION_CONTEXTUAL_CARD_ELIGIBILITY = 1686;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d2c39ea..da89116 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1575,8 +1575,7 @@
     public boolean isActiveNetworkMetered() {
         enforceAccessPermission();
 
-        final int uid = Binder.getCallingUid();
-        final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities;
+        final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
         if (caps != null) {
             return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         } else {
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 18c2722..d266635 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -38,9 +38,11 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.am.SettingsToPropertiesMapper;
 import com.android.server.utils.FlagNamespaceUtils;
 
 import java.io.File;
+import java.util.Arrays;
 
 /**
  * Utilities to help rescue the system from crash loops. Callers are expected to
@@ -158,6 +160,7 @@
      * opportunity to reset any settings depending on our rescue level.
      */
     public static void onSettingsProviderPublished(Context context) {
+        handleNativeRescuePartyResets();
         executeRescueLevel(context);
     }
 
@@ -176,6 +179,13 @@
         return SystemClock.elapsedRealtime();
     }
 
+    private static void handleNativeRescuePartyResets() {
+        if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
+            FlagNamespaceUtils.resetDeviceConfig(Settings.RESET_MODE_TRUSTED_DEFAULTS,
+                    Arrays.asList(SettingsToPropertiesMapper.getResetNativeCategories()));
+        }
+    }
+
     /**
      * Escalate to the next rescue level. After incrementing the level you'll
      * probably want to call {@link #executeRescueLevel(Context)}.
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c8e8ef8..e28d484 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1727,7 +1727,8 @@
         final List<AppOpsManager.PackageOps> pkgs = manager.getOpsForPackage(uid, packageName, ops);
         for (AppOpsManager.PackageOps pkg : CollectionUtils.emptyIfNull(pkgs)) {
             for (AppOpsManager.OpEntry op : CollectionUtils.emptyIfNull(pkg.getOps())) {
-                maxTime = Math.max(maxTime, op.getLastAccessTime());
+                maxTime = Math.max(maxTime, op.getLastAccessTime(
+                    AppOpsManager.OP_FLAGS_ALL_TRUSTED));
             }
         }
         return maxTime;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 545baa0..5cca159 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -594,8 +594,9 @@
                 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                         > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
                         && !vib.isNotification() && !vib.isRingtone()) {
-                    Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
-                            + uid + " is background");
+                    Slog.e(TAG, "Ignoring incoming vibration as process with"
+                            + " uid = " + uid + " is background,"
+                            + " usage = " + AudioAttributes.usageToString(vib.usageHint));
                     return;
                 }
                 linkVibration(vib);
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 8e2ca06..34fca23 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -81,7 +81,9 @@
         StringBuilder sb = new StringBuilder();
         sb.append("BroadcastFilter{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(" u");
+        sb.append(' ');
+        sb.append(owningUid);
+        sb.append("/u");
         sb.append(owningUserId);
         sb.append(' ');
         sb.append(receiverList);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index efb1c44..c1ed54e 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -445,7 +445,7 @@
         final long elapsed = finishTime - r.receiverTime;
         r.state = BroadcastRecord.IDLE;
         if (state == BroadcastRecord.IDLE) {
-            Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
+            Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
         }
         if (r.allowBackgroundActivityStarts && r.curApp != null) {
             if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
@@ -478,12 +478,13 @@
         if (!r.timeoutExempt) {
             if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
                 if (DEBUG_BROADCAST_DEFERRAL) {
-                    Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r);
+                    Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
+                            + " was slow: " + receiver + " br=" + r);
                 }
                 if (r.curApp != null) {
                     mDispatcher.startDeferring(r.curApp.uid);
                 } else {
-                    Slog.d(TAG, "finish receiver curApp is null? " + r);
+                    Slog.d(TAG_BROADCAST, "finish receiver curApp is null? " + r);
                 }
             }
         } else {
@@ -796,9 +797,7 @@
                     skipReceiverLocked(r);
                 }
             } else {
-                if (r.receiverTime == 0) {
-                    r.receiverTime = SystemClock.uptimeMillis();
-                }
+                r.receiverTime = SystemClock.uptimeMillis();
                 maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
                 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                         new Intent(r.intent), r.resultCode, r.resultData,
@@ -1083,16 +1082,19 @@
                         if (newCount == 0) {
                             // done!  clear out this record's bookkeeping and deliver
                             if (DEBUG_BROADCAST_DEFERRAL) {
-                                Slog.i(TAG, "Sending broadcast completion for split token "
-                                        + r.splitToken);
+                                Slog.i(TAG_BROADCAST,
+                                        "Sending broadcast completion for split token "
+                                        + r.splitToken + " : " + r.intent.getAction());
                             }
                             mSplitRefcounts.delete(r.splitToken);
                         } else {
                             // still have some split broadcast records in flight; update refcount
                             // and hold off on the callback
                             if (DEBUG_BROADCAST_DEFERRAL) {
-                                Slog.i(TAG, "Result refcount " + newCount + " for split token "
-                                        + r.splitToken + " - not sending completion yet");
+                                Slog.i(TAG_BROADCAST,
+                                        "Result refcount now " + newCount + " for split token "
+                                        + r.splitToken + " : " + r.intent.getAction()
+                                        + " - not sending completion yet");
                             }
                             sendResult = false;
                             mSplitRefcounts.put(r.splitToken, newCount);
@@ -1155,7 +1157,7 @@
                     BroadcastRecord defer;
                     if (r.nextReceiver + 1 == numReceivers) {
                         if (DEBUG_BROADCAST_DEFERRAL) {
-                            Slog.i(TAG, "Sole receiver of " + r
+                            Slog.i(TAG_BROADCAST, "Sole receiver of " + r
                                     + " is under deferral; setting aside and proceeding");
                         }
                         defer = r;
@@ -1185,15 +1187,25 @@
                                 // first split of this record; refcount for 'r' and 'deferred'
                                 r.splitToken = defer.splitToken = nextSplitTokenLocked();
                                 mSplitRefcounts.put(r.splitToken, 2);
+                                if (DEBUG_BROADCAST_DEFERRAL) {
+                                    Slog.i(TAG_BROADCAST,
+                                            "Broadcast needs split refcount; using new token "
+                                            + r.splitToken);
+                                }
                             } else {
                                 // new split from an already-refcounted situation; increment count
                                 final int curCount = mSplitRefcounts.get(token);
                                 if (DEBUG_BROADCAST_DEFERRAL) {
                                     if (curCount == 0) {
-                                        Slog.wtf(TAG, "Split refcount is zero with token for " + r);
+                                        Slog.wtf(TAG_BROADCAST,
+                                                "Split refcount is zero with token for " + r);
                                     }
                                 }
                                 mSplitRefcounts.put(token, curCount + 1);
+                                if (DEBUG_BROADCAST_DEFERRAL) {
+                                    Slog.i(TAG_BROADCAST, "New split count for token " + token
+                                            + " is " + (curCount + 1));
+                                }
                             }
                         }
                     }
@@ -1529,7 +1541,7 @@
         if (skip) {
             if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                     "Skipping delivery of ordered [" + mQueueName + "] "
-                    + r + " for whatever reason");
+                    + r + " for reason described above");
             r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
             r.receiver = null;
             r.curFilter = null;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index fa9b79d..1352504 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -332,7 +332,6 @@
                 }
                 splitReceivers.add(o);
                 receivers.remove(i);
-                break;
             } else {
                 i++;
             }
@@ -350,6 +349,7 @@
                 resultData, resultExtras, ordered, sticky, initialSticky, userId,
                 allowBackgroundActivityStarts, timeoutExempt);
 
+        split.splitToken = this.splitToken;
         return split;
     }
 
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 894a704..194549f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -78,11 +79,11 @@
     // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sDeviceConfigScopes = new String[] {
-        DeviceConfig.ActivityManagerNativeBoot.NAMESPACE,
-        DeviceConfig.MediaNative.NAMESPACE,
+        DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_MEDIA_NATIVE,
         DeviceConfig.NAMESPACE_NETD_NATIVE,
-        DeviceConfig.RuntimeNativeBoot.NAMESPACE,
+        DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
         DeviceConfig.RuntimeNative.NAMESPACE,
     };
 
@@ -167,7 +168,7 @@
      * booting.
      * @return
      */
-    public static String[] getResetNativeCategories() {
+    public static @NonNull String[] getResetNativeCategories() {
         if (!isNativeFlagsResetPerformed()) {
             return new String[0];
         }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 70c28a8..ba7288e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.appop;
 
+import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE;
+import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
+import static android.app.AppOpsManager.OP_FLAGS_ALL;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -23,12 +26,12 @@
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION;
-import static android.app.AppOpsManager.UID_STATE_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
 import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
 import static android.app.AppOpsManager.UID_STATE_TOP;
-import static android.app.AppOpsManager._NUM_UID_STATE;
 import static android.app.AppOpsManager.modeToName;
 import static android.app.AppOpsManager.opToName;
+import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -39,6 +42,9 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.HistoricalOps;
 import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.AppOpsManager.Mode;
+import android.app.AppOpsManager.OpEntry;
+import android.app.AppOpsManager.OpFlags;
 import android.app.AppOpsManagerInternal;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.content.BroadcastReceiver;
@@ -76,6 +82,8 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.KeyValueListParser;
+import android.util.LongSparseArray;
+import android.util.LongSparseLongArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -164,36 +172,6 @@
         UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_NONEXISTENT
     };
 
-    static final String[] UID_STATE_NAMES = new String[] {
-            "pers ",    // UID_STATE_PERSISTENT
-            "top  ",    // UID_STATE_TOP
-            "fgsvcl",   // UID_STATE_FOREGROUND_SERVICE_LOCATION
-            "fgsvc",    // UID_STATE_FOREGROUND_SERVICE
-            "fg   ",    // UID_STATE_FOREGROUND
-            "bg   ",    // UID_STATE_BACKGROUND
-            "cch  ",    // UID_STATE_CACHED
-    };
-
-    static final String[] UID_STATE_TIME_ATTRS = new String[] {
-            "tp",       // UID_STATE_PERSISTENT
-            "tt",       // UID_STATE_TOP
-            "tfsl",     // UID_STATE_FOREGROUND_SERVICE_LOCATION
-            "tfs",      // UID_STATE_FOREGROUND_SERVICE
-            "tf",       // UID_STATE_FOREGROUND
-            "tb",       // UID_STATE_BACKGROUND
-            "tc",       // UID_STATE_CACHED
-    };
-
-    static final String[] UID_STATE_REJECT_ATTRS = new String[] {
-            "rp",       // UID_STATE_PERSISTENT
-            "rt",       // UID_STATE_TOP
-            "rfsl",     // UID_STATE_FOREGROUND_SERVICE_LOCATION
-            "rfs",      // UID_STATE_FOREGROUND_SERVICE
-            "rf",       // UID_STATE_FOREGROUND
-            "rb",       // UID_STATE_BACKGROUND
-            "rc",       // UID_STATE_CACHED
-    };
-
     Context mContext;
     final AtomicFile mFile;
     final Handler mHandler;
@@ -355,12 +333,14 @@
 
         public boolean isDefault() {
             return (pkgOps == null || pkgOps.isEmpty())
-                    && (opModes == null || opModes.size() <= 0);
+                    && (opModes == null || opModes.size() <= 0)
+                    && (state == UID_STATE_CACHED
+                    && (pendingState == UID_STATE_CACHED));
         }
 
-        int evalMode(int mode) {
+        int evalMode(int op, int mode) {
             if (mode == AppOpsManager.MODE_FOREGROUND) {
-                return state <= UID_STATE_LAST_NON_RESTRICTED
+                return state <= AppOpsManager.resolveLastRestrictedUidState(op)
                         ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
             }
             return mode;
@@ -425,41 +405,121 @@
     }
 
     final static class Op {
+        int op;
+        boolean running;
         final UidState uidState;
-        final int uid;
-        final String packageName;
-        final int op;
-        int proxyUid = -1;
-        String proxyPackageName;
-        int mode;
-        int duration;
-        long time[] = new long[_NUM_UID_STATE];
-        long rejectTime[] = new long[_NUM_UID_STATE];
+        final @NonNull String packageName;
+
+        private @Mode int mode;
+        private @Nullable LongSparseLongArray mAccessTimes;
+        private @Nullable LongSparseLongArray mRejectTimes;
+        private @Nullable LongSparseLongArray mDurations;
+        private @Nullable LongSparseLongArray mProxyUids;
+        private @Nullable LongSparseArray<String> mProxyPackageNames;
+
         int startNesting;
         long startRealtime;
 
-        Op(UidState _uidState, String _packageName, int _op) {
-            uidState = _uidState;
-            uid = _uidState.uid;
-            packageName = _packageName;
-            op = _op;
-            mode = AppOpsManager.opToDefaultMode(op);
-        }
-
-        boolean hasAnyTime() {
-            for (int i = 0; i < AppOpsManager._NUM_UID_STATE; i++) {
-                if (time[i] != 0) {
-                    return true;
-                }
-                if (rejectTime[i] != 0) {
-                    return true;
-                }
-            }
-            return false;
+        Op(UidState uidState, String packageName, int op) {
+            this.op = op;
+            this.uidState = uidState;
+            this.packageName = packageName;
+            this.mode = AppOpsManager.opToDefaultMode(op);
         }
 
         int getMode() {
-            return uidState.evalMode(mode);
+            return mode;
+        }
+
+        int evalMode() {
+            return uidState.evalMode(op, mode);
+        }
+
+        /** @hide */
+        public void accessed(long time, int proxyUid, @Nullable String proxyPackageName,
+            @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);
+            if (mDurations != null) {
+                mDurations.delete(key);
+            }
+        }
+
+        /** @hide */
+        public void rejected(long time, int proxyUid, @Nullable String proxyPackageName,
+            @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);
+            if (mDurations != null) {
+                mDurations.delete(key);
+            }
+        }
+
+        /** @hide */
+        public void started(long time, @AppOpsManager.UidState int uidState, @OpFlags int flags) {
+            updateAccessTimeAndDuration(time, -1 /*duration*/, uidState, flags);
+            running = true;
+        }
+
+        /** @hide */
+        public void finished(long time, long duration, @AppOpsManager.UidState int uidState,
+            @OpFlags int flags) {
+            updateAccessTimeAndDuration(time, duration, uidState, flags);
+            running = false;
+        }
+
+        /** @hide */
+        public void running(long time, long duration, @AppOpsManager.UidState int uidState,
+            @OpFlags int flags) {
+            updateAccessTimeAndDuration(time, duration, uidState, flags);
+        }
+
+        /** @hide */
+        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,
+            @AppOpsManager.UidState int uidState, @OpFlags int flags) {
+            final long key = AppOpsManager.makeKey(uidState, flags);
+            if (mAccessTimes == null) {
+                mAccessTimes = new LongSparseLongArray();
+            }
+            mAccessTimes.put(key, time);
+            if (mDurations == null) {
+                mDurations = new LongSparseLongArray();
+            }
+            mDurations.put(key, duration);
+        }
+
+        private void updateProxyState(long key, int proxyUid,
+            @Nullable String proxyPackageName) {
+            if (mProxyUids == null) {
+                mProxyUids = new LongSparseLongArray();
+            }
+            mProxyUids.put(key, proxyUid);
+            if (mProxyPackageNames == null) {
+                mProxyPackageNames = new LongSparseArray<>();
+            }
+            mProxyPackageNames.put(key, proxyPackageName);
+        }
+
+        boolean hasAnyTime() {
+            return (mAccessTimes != null && mAccessTimes.size() > 0)
+                || (mRejectTimes != null && mRejectTimes.size() > 0);
         }
     }
 
@@ -812,7 +872,7 @@
                 final int opCount = client.mStartedOps.size();
                 for (int j = opCount - 1; j >= 0; j--) {
                     final Op op = client.mStartedOps.get(j);
-                    if (uid == op.uid && packageName.equals(op.packageName)) {
+                    if (uid == op.uidState.uid && packageName.equals(op.packageName)) {
                         finishOperationLocked(op, /*finishNested*/ true);
                         client.mStartedOps.remove(j);
                         if (op.startNesting <= 0) {
@@ -829,9 +889,9 @@
                 final int opCount = ops.size();
                 for (int i = 0; i < opCount; i++) {
                     final Op op = ops.valueAt(i);
-                    if (op.duration == -1) {
+                    if (op.running) {
                         scheduleOpActiveChangedIfNeededLocked(
-                                op.op, op.uid, op.packageName, false);
+                                op.op, op.uidState.uid, op.packageName, false);
                     }
                 }
             }
@@ -854,7 +914,7 @@
             if (uidState != null && uidState.pendingState != newState) {
                 final int oldPendingState = uidState.pendingState;
                 uidState.pendingState = newState;
-                if (newState < uidState.state || newState <= UID_STATE_LAST_NON_RESTRICTED) {
+                if (newState < uidState.state || newState <= UID_STATE_MAX_LAST_NON_RESTRICTED) {
                     // We are moving to a more important state, or the new state is in the
                     // foreground, then always do it immediately.
                     commitUidPendingStateLocked(uidState);
@@ -880,8 +940,18 @@
                         for (int j = ops.size() - 1; j >= 0; j--) {
                             final Op op = ops.valueAt(j);
                             if (op.startNesting > 0) {
-                                op.time[oldPendingState] = now;
-                                op.time[newState] = now;
+                                final long duration = SystemClock.elapsedRealtime()
+                                        - op.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
+                                op.finished(now, duration, oldPendingState,
+                                        AppOpsManager.OP_FLAG_SELF);
+                                // Start the op in the new state
+                                op.startRealtime = now;
+                                op.started(now, newState, AppOpsManager.OP_FLAG_SELF);
                             }
                         }
                     }
@@ -911,13 +981,7 @@
             resOps = new ArrayList<>();
             for (int j=0; j<pkgOps.size(); j++) {
                 Op curOp = pkgOps.valueAt(j);
-                final boolean running = curOp.duration == -1;
-                long duration = running
-                        ? (elapsedNow - curOp.startRealtime)
-                        : curOp.duration;
-                resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
-                        curOp.rejectTime, (int) duration, running, curOp.proxyUid,
-                        curOp.proxyPackageName));
+                resOps.add(getOpEntryForResult(curOp, elapsedNow));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
@@ -926,13 +990,7 @@
                     if (resOps == null) {
                         resOps = new ArrayList<>();
                     }
-                    final boolean running = curOp.duration == -1;
-                    final long duration = running
-                            ? (elapsedNow - curOp.startRealtime)
-                            : curOp.duration;
-                    resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
-                            curOp.rejectTime, (int) duration, running, curOp.proxyUid,
-                            curOp.proxyPackageName));
+                    resOps.add(getOpEntryForResult(curOp, elapsedNow));
                 }
             }
         }
@@ -947,8 +1005,7 @@
         if (ops == null) {
             resOps = new ArrayList<>();
             for (int j=0; j<uidOps.size(); j++) {
-                resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(j), uidOps.valueAt(j),
-                        0, 0, 0, -1, null));
+                resOps.add(new OpEntry(uidOps.keyAt(j), uidOps.valueAt(j)));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
@@ -957,14 +1014,27 @@
                     if (resOps == null) {
                         resOps = new ArrayList<>();
                     }
-                    resOps.add(new AppOpsManager.OpEntry(uidOps.keyAt(index), uidOps.valueAt(index),
-                            0, 0, 0, -1, null));
+                    resOps.add(new OpEntry(uidOps.keyAt(j), uidOps.valueAt(j)));
                 }
             }
         }
         return resOps;
     }
 
+    private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) {
+        if (op.running) {
+            op.continuing(elapsedNow - op.startRealtime,
+                op.uidState.state, AppOpsManager.OP_FLAG_SELF);
+        }
+        final OpEntry entry = new OpEntry(op.op, op.running, op.mode,
+            op.mAccessTimes != null ? op.mAccessTimes.clone() : null,
+            op.mRejectTimes != null ? op.mRejectTimes.clone() : null,
+            op.mDurations != null ? op.mDurations.clone() : null,
+            op.mProxyUids != null ? op.mProxyUids.clone() : null,
+            op.mProxyPackageNames != null ? op.mProxyPackageNames.clone() : null);
+        return entry;
+    }
+
     @Override
     public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
@@ -1026,13 +1096,14 @@
     @Override
     public void getHistoricalOps(int uid, @NonNull String packageName,
             @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
-            @NonNull RemoteCallback callback) {
+            @OpFlags int flags, @NonNull RemoteCallback callback) {
         // Use the builder to validate arguments.
-        final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
+        new HistoricalOpsRequest.Builder(
                 beginTimeMillis, endTimeMillis)
                 .setUid(uid)
                 .setPackageName(packageName)
                 .setOpNames(opNames)
+                .setFlags(flags)
                 .build();
         Preconditions.checkNotNull(callback, "callback cannot be null");
 
@@ -1041,79 +1112,23 @@
 
         final String[] opNamesArray = (opNames != null)
                 ? opNames.toArray(new String[opNames.size()]) : null;
-        if (mHistoricalRegistry.getMode() == AppOpsManager.HISTORICAL_MODE_DISABLED) {
-            // TODO (bug:122218838): Remove once the feature fully enabled.
-            getHistoricalPackagesOpsCompat(uid, packageName, opNamesArray, beginTimeMillis,
-                    endTimeMillis, callback);
-        } else {
-            // Must not hold the appops lock
-            mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray,
-                    beginTimeMillis, endTimeMillis, callback);
-        }
-    }
 
-    private void getHistoricalPackagesOpsCompat(int uid, @NonNull String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
-            @NonNull RemoteCallback callback) {
-        synchronized (AppOpsService.this) {
-            final HistoricalOps ops = new HistoricalOps(beginTimeMillis, endTimeMillis);
-            if (opNames == null) {
-                opNames = AppOpsManager.getOpStrs();
-            }
-            final int uidStateCount = mUidStates.size();
-            for (int uidIdx = 0; uidIdx < uidStateCount; uidIdx++) {
-                final UidState uidState = mUidStates.valueAt(uidIdx);
-                if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()
-                        || (uid != Process.INVALID_UID && uid != uidState.uid)) {
-                    continue;
-                }
-                final ArrayMap<String, Ops> packages = uidState.pkgOps;
-                final int packageCount = packages.size();
-                for (int pkgIdx = 0; pkgIdx < packageCount; pkgIdx++) {
-                    final Ops pkgOps = packages.valueAt(pkgIdx);
-                    if (packageName != null && !packageName.equals(pkgOps.packageName)) {
-                        continue;
-                    }
-                    final int opCount = opNames.length;
-                    for (int opIdx = 0; opIdx < opCount; opIdx++) {
-                        final String opName = opNames[opIdx];
-                        if (!ArrayUtils.contains(opNames, opName)) {
-                            continue;
-                        }
-                        final int opCode = AppOpsManager.strOpToOp(opName);
-                        final Op op = pkgOps.get(opCode);
-                        if (op == null) {
-                            continue;
-                        }
-                        final int stateCount = AppOpsManager._NUM_UID_STATE;
-                        for (int stateIdx = 0; stateIdx < stateCount; stateIdx++) {
-                            if (op.rejectTime[stateIdx] != 0) {
-                                ops.increaseRejectCount(opCode, uidState.uid,
-                                        pkgOps.packageName, stateIdx, 1);
-                            } else if (op.time[stateIdx] != 0) {
-                                ops.increaseAccessCount(opCode, uidState.uid,
-                                        pkgOps.packageName, stateIdx, 1);
-                            }
-                        }
-                    }
-                }
-            }
-            final Bundle payload = new Bundle();
-            payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, ops);
-            callback.sendResult(payload);
-        }
+        // Must not hold the appops lock
+        mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray,
+                beginTimeMillis, endTimeMillis, flags, callback);
     }
 
     @Override
     public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
             @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
-            @NonNull RemoteCallback callback) {
+            @OpFlags int flags, @NonNull RemoteCallback callback) {
         // Use the builder to validate arguments.
-        final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder(
+        new HistoricalOpsRequest.Builder(
                 beginTimeMillis, endTimeMillis)
                 .setUid(uid)
                 .setPackageName(packageName)
                 .setOpNames(opNames)
+                .setFlags(flags)
                 .build();
         Preconditions.checkNotNull(callback, "callback cannot be null");
 
@@ -1125,7 +1140,7 @@
 
         // Must not hold the appops lock
         mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray,
-                beginTimeMillis, endTimeMillis, callback);
+                beginTimeMillis, endTimeMillis, flags, callback);
     }
 
     @Override
@@ -1172,7 +1187,7 @@
         }
     }
 
-    void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) {
+    private void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) {
         if (callingPid == Process.myPid()) {
             return;
         }
@@ -1316,6 +1331,8 @@
                 return;
             }
 
+            boolean scheduleWrite = false;
+
             int numPkgs = pkgOps.size();
             for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
                 Ops ops = pkgOps.valueAt(pkgNum);
@@ -1327,14 +1344,14 @@
 
                 int defaultMode = AppOpsManager.opToDefaultMode(code);
                 if (op.mode != defaultMode) {
-                    Slog.w(TAG, "resetting app-op mode for " + AppOpsManager.opToName(code) + " of "
-                            + pkgOps.keyAt(pkgNum));
-
                     op.mode = defaultMode;
-
-                    scheduleWriteLocked();
+                    scheduleWrite = true;
                 }
             }
+
+            if (scheduleWrite) {
+                scheduleWriteLocked();
+            }
         }
     }
 
@@ -1544,9 +1561,10 @@
                             curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
                             changed = true;
                             uidChanged = true;
-                            callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
+                            final int uid = curOp.uidState.uid;
+                            callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
                                     mOpModeWatchers.get(curOp.op));
-                            callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
+                            callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
                                     mPackageModeWatchers.get(packageName));
                             if (!curOp.hasAnyTime()) {
                                 pkgOps.removeAt(j);
@@ -1727,6 +1745,7 @@
     private int checkOperationUnchecked(int code, int uid, String packageName,
                 boolean raw) {
         synchronized (this) {
+            checkPackage(uid, packageName);
             if (isOpRestrictedLocked(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
@@ -1735,13 +1754,13 @@
             if (uidState != null && uidState.opModes != null
                     && uidState.opModes.indexOfKey(code) >= 0) {
                 final int rawMode = uidState.opModes.get(code);
-                return raw ? rawMode : uidState.evalMode(rawMode);
+                return raw ? rawMode : uidState.evalMode(code, rawMode);
             }
             Op op = getOpLocked(code, uid, packageName, false, true, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
-            return op.mode;
+            return raw ? op.mode : op.evalMode();
         }
     }
 
@@ -1855,21 +1874,32 @@
             String proxyPackageName, int proxiedUid, String proxiedPackageName) {
         verifyIncomingUid(proxyUid);
         verifyIncomingOp(code);
+
         String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
         if (resolveProxyPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
         }
+
+        final boolean isProxyTrusted = mContext.checkPermission(
+                Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
+                == PackageManager.PERMISSION_GRANTED;
+
+        final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
+                : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
         final int proxyMode = noteOperationUnchecked(code, proxyUid,
-                resolveProxyPackageName, -1, null);
+                resolveProxyPackageName, Process.INVALID_UID, null, proxyFlags);
         if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
             return proxyMode;
         }
+
         String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
         if (resolveProxiedPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
         }
+        final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
+                : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
         return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
-                proxyMode, resolveProxyPackageName);
+                proxyUid, resolveProxyPackageName, proxiedFlags);
     }
 
     @Override
@@ -1892,11 +1922,12 @@
         if (resolvedPackageName == null) {
             return AppOpsManager.MODE_IGNORED;
         }
-        return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
+        return noteOperationUnchecked(code, uid, resolvedPackageName, Process.INVALID_UID, null,
+                AppOpsManager.OP_FLAG_SELF);
     }
 
     private int noteOperationUnchecked(int code, int uid, String packageName,
-            int proxyUid, String proxyPackageName) {
+            int proxyUid, String proxyPackageName, @OpFlags int flags) {
         synchronized (this) {
             final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
                     false /* uidMismatchExpected */);
@@ -1914,49 +1945,52 @@
                 return AppOpsManager.MODE_IGNORED;
             }
             final UidState uidState = ops.uidState;
-            if (op.duration == -1) {
+            if (op.running) {
+                final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
+                    op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
-                        + " code " + code + " time=" + op.time[uidState.state]
-                        + " duration=" + op.duration);
+                        + " code " + code + " time=" + entry.getLastAccessTime(uidState.state,
+                        uidState.state, flags) + " duration=" + entry.getLastDuration(
+                                uidState.state, uidState.state, flags));
             }
-            op.duration = 0;
+
             final int switchCode = AppOpsManager.opToSwitch(code);
             // If there is a non-default per UID policy (we set UID op mode only if
             // non-default) it takes over, otherwise use the per package policy.
             if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
-                final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode));
+                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
-                    op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
+                            uidState.state, flags);
+                    mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
+                            uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
-                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
-                            uidState.state);
                     return uidMode;
                 }
             } else {
                 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
-                final int mode = switchOp.getMode();
-                if (mode != AppOpsManager.MODE_ALLOWED) {
+                final int mode = switchOp.evalMode();
+                if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
-                    op.rejectTime[uidState.state] = System.currentTimeMillis();
-                    scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode);
-                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
-                            uidState.state);
+                    op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
+                            uidState.state, flags);
+                    mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
+                            uidState.state, flags);
+                    scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
                     return mode;
                 }
             }
             if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                     + " package " + packageName);
-            op.time[uidState.state] = System.currentTimeMillis();
+            op.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName,
+                    uidState.state, flags);
             mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
-                    uidState.state);
-            op.rejectTime[uidState.state] = 0;
-            op.proxyUid = proxyUid;
-            op.proxyPackageName = proxyPackageName;
+                    uidState.state, flags);
             scheduleOpNotedIfNeededLocked(code, uid, packageName,
                     AppOpsManager.MODE_ALLOWED);
             return AppOpsManager.MODE_ALLOWED;
@@ -2080,29 +2114,34 @@
             final UidState uidState = ops.uidState;
             // If there is a non-default per UID policy (we set UID op mode only if
             // non-default) it takes over, otherwise use the per package policy.
+            final int opCode = op.op;
             if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
-                final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode));
+                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
                 if (uidMode != AppOpsManager.MODE_ALLOWED
                         && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
-                    op.rejectTime[uidState.state] = System.currentTimeMillis();
-                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
-                            uidState.state);
+                    // We don't support proxy long running ops (start/stop)
+                    op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
+                            null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF);
+                    mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
+                            uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return uidMode;
                 }
             } else {
                 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
-                final int mode = switchOp.getMode();
+                final int mode = switchOp.evalMode();
                 if (mode != AppOpsManager.MODE_ALLOWED
                         && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) {
                     if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
-                    op.rejectTime[uidState.state] = System.currentTimeMillis();
-                    mHistoricalRegistry.incrementOpRejected(op.op, uid, packageName,
-                            uidState.state);
+                    // We don't support proxy long running ops (start/stop)
+                    op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
+                            null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF);
+                    mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
+                            uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return mode;
                 }
             }
@@ -2110,11 +2149,12 @@
                     + " package " + resolvedPackageName);
             if (op.startNesting == 0) {
                 op.startRealtime = SystemClock.elapsedRealtime();
-                op.time[uidState.state] = System.currentTimeMillis();
-                mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
-                        uidState.state);
-                op.rejectTime[uidState.state] = 0;
-                op.duration = -1;
+                // We don't support proxy long running ops (start/stop)
+                op.started(System.currentTimeMillis(), uidState.state,
+                        AppOpsManager.OP_FLAG_SELF);
+                mHistoricalRegistry.incrementOpAccessedCount(opCode, uid, packageName,
+                        uidState.state, AppOpsManager.OP_FLAG_SELF);
+
                 scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
             }
             op.startNesting++;
@@ -2161,7 +2201,7 @@
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
-                Slog.wtf(TAG, "Operation not started: uid=" + op.uid + " pkg="
+                Slog.wtf(TAG, "Operation not started: uid=" + op.uidState.uid + " pkg="
                         + op.packageName + " op=" + AppOpsManager.opToName(op.op));
                 return;
             }
@@ -2272,16 +2312,24 @@
     }
 
     void finishOperationLocked(Op op, boolean finishNested) {
+        final int opCode = op.op;
+        final int uid = op.uidState.uid;
         if (op.startNesting <= 1 || finishNested) {
             if (op.startNesting == 1 || finishNested) {
-                op.duration = (int)(SystemClock.elapsedRealtime() - op.startRealtime);
-                mHistoricalRegistry.increaseOpAccessDuration(op.op, op.uid, op.packageName,
-                        op.uidState.state, op.duration);
-                op.time[op.uidState.state] = System.currentTimeMillis();
+                // We don't support proxy long running ops (start/stop)
+                final long duration = SystemClock.elapsedRealtime() - op.startRealtime;
+                op.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 {
-                Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
-                        + op.packageName + " code " + op.op + " time=" + op.time
-                        + " duration=" + op.duration + " nesting=" + op.startNesting);
+                final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
+                    op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
+                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=" + op.startNesting);
             }
             if (op.startNesting >= 1) {
                 op.uidState.startNesting -= op.startNesting;
@@ -2311,7 +2359,7 @@
         throw new IllegalArgumentException("Bad operation #" + op);
     }
 
-    private UidState getUidStateLocked(int uid, boolean edit) {
+    private @NonNull UidState getUidStateLocked(int uid, boolean edit) {
         UidState uidState = mUidStates.get(uid);
         if (uidState == null) {
             if (!edit) {
@@ -2335,8 +2383,8 @@
     }
 
     private void commitUidPendingStateLocked(UidState uidState) {
-        final boolean lastForeground = uidState.state <= UID_STATE_LAST_NON_RESTRICTED;
-        final boolean nowForeground = uidState.pendingState <= UID_STATE_LAST_NON_RESTRICTED;
+        final boolean lastForeground = uidState.state <= UID_STATE_MAX_LAST_NON_RESTRICTED;
+        final boolean nowForeground = uidState.pendingState <= UID_STATE_MAX_LAST_NON_RESTRICTED;
         uidState.state = uidState.pendingState;
         uidState.pendingStateCommitTime = 0;
         if (uidState.hasForegroundWatchers && lastForeground != nowForeground) {
@@ -2345,7 +2393,15 @@
                     continue;
                 }
                 final int code = uidState.foregroundOps.keyAt(fgi);
-
+                // For location ops we consider fg state only if the fg service
+                // is of location type, for all other ops any fg service will do.
+                final long resolvedLastRestrictedUidState = resolveFirstUnrestrictedUidState(code);
+                final boolean resolvedLastFg = uidState.state <= resolvedLastRestrictedUidState;
+                final boolean resolvedNowBg = uidState.pendingState
+                        <= resolvedLastRestrictedUidState;
+                if (resolvedLastFg == resolvedNowBg) {
+                    continue;
+                }
                 final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
                 if (callbacks != null) {
                     for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) {
@@ -2360,8 +2416,10 @@
                         if (uidState.pkgOps != null) {
                             for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
                                 final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
-                                if (doAllPackages || (op != null
-                                        && op.mode == AppOpsManager.MODE_FOREGROUND)) {
+                                if (op == null) {
+                                    continue;
+                                }
+                                if (doAllPackages || op.mode == AppOpsManager.MODE_FOREGROUND) {
                                     mHandler.sendMessage(PooledLambda.obtainMessage(
                                             AppOpsService::notifyOpChanged,
                                             this, callback, code, uidState.uid,
@@ -2651,7 +2709,7 @@
                 final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
                 if (idx >= 0) {
                     uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
-                            uidState.opModes.valueAt(idx));
+                        uidState.opModes.valueAt(idx));
                 }
             }
             if (uidState.pkgOps == null) {
@@ -2664,7 +2722,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);
                         copy.mode = op.mode;
                         ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
                         changed = true;
@@ -2692,7 +2750,7 @@
         scheduleFastWriteLocked();
     }
 
-    void readUidOps(XmlPullParser parser) throws NumberFormatException,
+    private void readUidOps(XmlPullParser parser) throws NumberFormatException,
             XmlPullParserException, IOException {
         final int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
         int outerDepth = parser.getDepth();
@@ -2720,8 +2778,8 @@
         }
     }
 
-    void readPackage(XmlPullParser parser) throws NumberFormatException,
-            XmlPullParserException, IOException {
+    private void readPackage(XmlPullParser parser)
+            throws NumberFormatException, XmlPullParserException, IOException {
         String pkgName = parser.getAttributeValue(null, "n");
         int outerDepth = parser.getDepth();
         int type;
@@ -2742,9 +2800,10 @@
         }
     }
 
-    void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
-            XmlPullParserException, IOException {
+    private void readUid(XmlPullParser parser, String pkgName)
+            throws NumberFormatException, XmlPullParserException, IOException {
         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        final UidState uidState = getUidStateLocked(uid, true);
         String isPrivilegedString = parser.getAttributeValue(null, "p");
         boolean isPrivileged = false;
         if (isPrivilegedString == null) {
@@ -2774,113 +2833,73 @@
             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                 continue;
             }
-
             String tagName = parser.getName();
             if (tagName.equals("op")) {
-                UidState uidState = getUidStateLocked(uid, true);
-                if (uidState.pkgOps == null) {
-                    uidState.pkgOps = new ArrayMap<>();
-                }
-
-                Op op = new Op(uidState, pkgName,
-                        Integer.parseInt(parser.getAttributeValue(null, "n")));
-
-                for (int i = parser.getAttributeCount()-1; i >= 0; i--) {
-                    final String name = parser.getAttributeName(i);
-                    final String value = parser.getAttributeValue(i);
-                    switch (name) {
-                        case "m":
-                            op.mode = Integer.parseInt(value);
-                            break;
-                        case "d":
-                            op.duration = Integer.parseInt(value);
-                            break;
-                        case "pu":
-                            op.proxyUid = Integer.parseInt(value);
-                            break;
-                        case "pp":
-                            op.proxyPackageName = value;
-                            break;
-                        case "tp":
-                            op.time[AppOpsManager.UID_STATE_PERSISTENT] = Long.parseLong(value);
-                            break;
-                        case "tt":
-                            op.time[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
-                            break;
-                        case "tfsl":
-                            op.time[AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION] =
-                                    Long.parseLong(value);
-                            break;
-                        case "tfs":
-                            op.time[AppOpsManager.UID_STATE_FOREGROUND_SERVICE] =
-                                    Long.parseLong(value);
-                            break;
-                        case "tf":
-                            op.time[AppOpsManager.UID_STATE_FOREGROUND] = Long.parseLong(value);
-                            break;
-                        case "tb":
-                            op.time[AppOpsManager.UID_STATE_BACKGROUND] = Long.parseLong(value);
-                            break;
-                        case "tc":
-                            op.time[AppOpsManager.UID_STATE_CACHED] = Long.parseLong(value);
-                            break;
-                        case "rp":
-                            op.rejectTime[AppOpsManager.UID_STATE_PERSISTENT] =
-                                    Long.parseLong(value);
-                            break;
-                        case "rt":
-                            op.rejectTime[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
-                            break;
-                        case "rfsl":
-                            op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION] =
-                                    Long.parseLong(value);
-                            break;
-                        case "rfs":
-                            op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND_SERVICE] =
-                                    Long.parseLong(value);
-                            break;
-                        case "rf":
-                            op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND] =
-                                    Long.parseLong(value);
-                            break;
-                        case "rb":
-                            op.rejectTime[AppOpsManager.UID_STATE_BACKGROUND] =
-                                    Long.parseLong(value);
-                            break;
-                        case "rc":
-                            op.rejectTime[AppOpsManager.UID_STATE_CACHED] =
-                                    Long.parseLong(value);
-                            break;
-                        case "t":
-                            // Backwards compat.
-                            op.time[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
-                            break;
-                        case "r":
-                            // Backwards compat.
-                            op.rejectTime[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
-                            break;
-                        default:
-                            Slog.w(TAG, "Unknown attribute in 'op' tag: " + name);
-                            break;
-                    }
-                }
-
-                Ops ops = uidState.pkgOps.get(pkgName);
-                if (ops == null) {
-                    ops = new Ops(pkgName, uidState, isPrivileged);
-                    uidState.pkgOps.put(pkgName, ops);
-                }
-                ops.put(op.op, op);
+                readOp(parser, uidState, pkgName, isPrivileged);
             } else {
                 Slog.w(TAG, "Unknown element under <pkg>: "
                         + parser.getName());
                 XmlUtils.skipCurrentTag(parser);
             }
         }
-        UidState uidState = getUidStateLocked(uid, false);
-        if (uidState != null) {
-            uidState.evalForegroundOps(mOpModeWatchers);
+        uidState.evalForegroundOps(mOpModeWatchers);
+    }
+
+    private void readOp(XmlPullParser parser, @NonNull UidState uidState,
+            @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
+            XmlPullParserException, IOException {
+        Op op = new Op(uidState, pkgName,
+                Integer.parseInt(parser.getAttributeValue(null, "n")));
+
+        final int mode = XmlUtils.readIntAttribute(parser, "m",
+                AppOpsManager.opToDefaultMode(op.op));
+        op.mode = mode;
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals("st")) {
+                final long key = XmlUtils.readLongAttribute(parser, "n");
+
+                final int flags = AppOpsManager.extractFlagsFromKey(key);
+                final int state = AppOpsManager.extractUidStateFromKey(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 String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
+                final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", 0);
+
+                if (accessTime > 0) {
+                    op.accessed(accessTime, proxyUid, proxyPkg, state, flags);
+                }
+                if (rejectTime > 0) {
+                    op.rejected(rejectTime, proxyUid, proxyPkg, state, flags);
+                }
+                if (accessDuration > 0) {
+                    op.running(accessTime, accessDuration, state, flags);
+                }
+            } else {
+                Slog.w(TAG, "Unknown element under <op>: "
+                        + parser.getName());
+                XmlUtils.skipCurrentTag(parser);
+            }
         }
+
+        if (uidState.pkgOps == null) {
+            uidState.pkgOps = new ArrayMap<>();
+        }
+        Ops ops = uidState.pkgOps.get(pkgName);
+        if (ops == null) {
+            ops = new Ops(pkgName, uidState, isPrivileged);
+            uidState.pkgOps.put(pkgName, ops);
+        }
+        ops.put(op.op, op);
     }
 
     void writeState() {
@@ -2955,30 +2974,53 @@
                             if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
                                 out.attribute(null, "m", Integer.toString(op.getMode()));
                             }
-                            for (int k = 0; k < _NUM_UID_STATE; k++) {
-                                final long time = op.getLastTimeFor(k);
-                                if (time != 0) {
-                                    out.attribute(null, UID_STATE_TIME_ATTRS[k],
-                                            Long.toString(time));
+
+                            final LongSparseArray keys = op.collectKeys();
+                            if (keys == null || keys.size() <= 0) {
+                                continue;
+                            }
+
+                            final int keyCount = keys.size();
+                            for (int k = 0; k < keyCount; k++) {
+                                final long key = keys.keyAt(k);
+
+                                final int uidState = AppOpsManager.extractUidStateFromKey(key);
+                                final int flags = AppOpsManager.extractFlagsFromKey(key);
+
+                                final long accessTime = op.getLastAccessTime(
+                                        uidState, uidState, flags);
+                                final long rejectTime = op.getLastRejectTime(
+                                        uidState, uidState, flags);
+                                final long accessDuration = op.getLastDuration(
+                                        uidState, uidState, flags);
+                                final String proxyPkg = op.getProxyPackageName(uidState, flags);
+                                final int proxyUid = op.getProxyUid(uidState, flags);
+
+                                if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
+                                        && proxyPkg == null && proxyUid < 0) {
+                                    continue;
                                 }
-                                final long rejectTime = op.getLastRejectTimeFor(k);
-                                if (rejectTime != 0) {
-                                    out.attribute(null, UID_STATE_REJECT_ATTRS[k],
-                                            Long.toString(rejectTime));
+
+                                out.startTag(null, "st");
+                                out.attribute(null, "n", Long.toString(key));
+                                if (accessTime > 0) {
+                                    out.attribute(null, "t", Long.toString(accessTime));
                                 }
+                                if (rejectTime > 0) {
+                                    out.attribute(null, "r", Long.toString(rejectTime));
+                                }
+                                if (accessDuration > 0) {
+                                    out.attribute(null, "d", Long.toString(accessDuration));
+                                }
+                                if (proxyPkg != null) {
+                                    out.attribute(null, "pp", proxyPkg);
+                                }
+                                if (proxyUid >= 0) {
+                                    out.attribute(null, "pu", Integer.toString(proxyUid));
+                                }
+                                out.endTag(null, "st");
                             }
-                            int dur = op.getDuration();
-                            if (dur != 0) {
-                                out.attribute(null, "d", Integer.toString(dur));
-                            }
-                            int proxyUid = op.getProxyUid();
-                            if (proxyUid != -1) {
-                                out.attribute(null, "pu", Integer.toString(proxyUid));
-                            }
-                            String proxyPackageName = op.getProxyPackageName();
-                            if (proxyPackageName != null) {
-                                out.attribute(null, "pp", proxyPackageName);
-                            }
+
                             out.endTag(null, "op");
                         }
                         out.endTag(null, "uid");
@@ -3462,32 +3504,80 @@
         pw.println("    Limit output to data associated with the given package name.");
         pw.println("  --watchers");
         pw.println("    Only output the watcher sections.");
+        pw.println("  --history");
+        pw.println("    Output the historical data.");
     }
 
-    private void dumpTimesLocked(PrintWriter pw, String firstPrefix, String prefix, long[] times,
-            long now, SimpleDateFormat sdf, Date date) {
-        boolean hasTime = false;
-        for (int i = 0; i < _NUM_UID_STATE; i++) {
-            if (times[i] != 0) {
-                hasTime = true;
-                break;
-            }
-        }
-        if (!hasTime) {
+    private void dumpStatesLocked(@NonNull PrintWriter pw, @NonNull Op op,
+            long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
+
+        final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
+            op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
+
+        final LongSparseArray keys = entry.collectKeys();
+        if (keys == null || keys.size() <= 0) {
             return;
         }
-        boolean first = true;
-        for (int i = 0; i < _NUM_UID_STATE; i++) {
-            if (times[i] != 0) {
-                pw.print(first ? firstPrefix : prefix);
-                first = false;
-                pw.print(UID_STATE_NAMES[i]);
-                pw.print(" = ");
-                date.setTime(times[i]);
+
+        final int keyCount = keys.size();
+        for (int k = 0; k < keyCount; k++) {
+            final long key = keys.keyAt(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 int proxyUid = entry.getProxyUid(uidState, flags);
+
+            if (accessTime > 0) {
+                pw.print(prefix);
+                pw.print("Access: ");
+                pw.print(AppOpsManager.keyToString(key));
+                pw.print(" ");
+                date.setTime(accessTime);
                 pw.print(sdf.format(date));
                 pw.print(" (");
-                TimeUtils.formatDuration(times[i]-now, pw);
-                pw.println(")");
+                TimeUtils.formatDuration(accessTime - now, pw);
+                pw.print(")");
+                if (accessDuration > 0) {
+                    pw.print(" duration=");
+                    TimeUtils.formatDuration(accessDuration, pw);
+                }
+                if (proxyUid >= 0) {
+                    pw.print(" proxy[");
+                    pw.print("uid=");
+                    pw.print(proxyUid);
+                    pw.print(", pkg=");
+                    pw.print(proxyPkg);
+                    pw.print("]");
+                }
+                pw.println();
+            }
+
+            if (rejectTime > 0) {
+                pw.print(prefix);
+                pw.print("Reject: ");
+                pw.print(AppOpsManager.keyToString(key));
+                date.setTime(rejectTime);
+                pw.print(sdf.format(date));
+                pw.print(" (");
+                TimeUtils.formatDuration(rejectTime - now, pw);
+                pw.print(")");
+                if (proxyUid >= 0) {
+                    pw.print(" proxy[");
+                    pw.print("uid=");
+                    pw.print(proxyUid);
+                    pw.print(", pkg=");
+                    pw.print(proxyPkg);
+                    pw.print("]");
+                }
+                pw.println();
             }
         }
     }
@@ -3501,6 +3591,7 @@
         int dumpUid = Process.INVALID_UID;
         int dumpMode = -1;
         boolean dumpWatchers = false;
+        boolean dumpHistory = false;
 
         if (args != null) {
             for (int i=0; i<args.length; i++) {
@@ -3550,6 +3641,8 @@
                     }
                 } else if ("--watchers".equals(arg)) {
                     dumpWatchers = true;
+                } else if ("--history".equals(arg)) {
+                    dumpHistory = true;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
                     pw.println("Unknown option: " + arg);
                     return;
@@ -3562,7 +3655,9 @@
 
         synchronized (this) {
             pw.println("Current AppOps Service state:");
-            mConstants.dump(pw);
+            if (!dumpHistory && !dumpWatchers) {
+                mConstants.dump(pw);
+            }
             pw.println();
             final long now = System.currentTimeMillis();
             final long nowElapsed = SystemClock.elapsedRealtime();
@@ -3571,7 +3666,7 @@
             final Date date = new Date();
             boolean needSep = false;
             if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null
-                    && !dumpWatchers) {
+                    && !dumpWatchers && !dumpHistory) {
                 pw.println("  Profile owners:");
                 for (int poi = 0; poi < mProfileOwners.size(); poi++) {
                     pw.print("    User #");
@@ -3582,7 +3677,7 @@
                 }
                 pw.println();
             }
-            if (mOpModeWatchers.size() > 0) {
+            if (mOpModeWatchers.size() > 0 && !dumpHistory) {
                 boolean printedHeader = false;
                 for (int i=0; i<mOpModeWatchers.size(); i++) {
                     if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) {
@@ -3612,7 +3707,7 @@
                     }
                 }
             }
-            if (mPackageModeWatchers.size() > 0 && dumpOp < 0) {
+            if (mPackageModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) {
                 boolean printedHeader = false;
                 for (int i=0; i<mPackageModeWatchers.size(); i++) {
                     if (dumpPackage != null && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) {
@@ -3632,7 +3727,7 @@
                     }
                 }
             }
-            if (mModeWatchers.size() > 0 && dumpOp < 0) {
+            if (mModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) {
                 boolean printedHeader = false;
                 for (int i=0; i<mModeWatchers.size(); i++) {
                     final ModeCallback cb = mModeWatchers.valueAt(i);
@@ -3730,7 +3825,7 @@
                     pw.println(cb);
                 }
             }
-            if (mClients.size() > 0 && dumpMode < 0 && !dumpWatchers) {
+            if (mClients.size() > 0 && dumpMode < 0 && !dumpWatchers && !dumpHistory) {
                 needSep = true;
                 boolean printedHeader = false;
                 for (int i=0; i<mClients.size(); i++) {
@@ -3759,7 +3854,7 @@
                                 pw.println("      Started ops:");
                                 printedStarted = true;
                             }
-                            pw.print("        "); pw.print("uid="); pw.print(op.uid);
+                            pw.print("        "); pw.print("uid="); pw.print(op.uidState.uid);
                             pw.print(" pkg="); pw.print(op.packageName);
                             pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
                         }
@@ -3767,7 +3862,7 @@
                 }
             }
             if (mAudioRestrictions.size() > 0 && dumpOp < 0 && dumpPackage != null
-                    && dumpMode < 0 && !dumpWatchers) {
+                    && dumpMode < 0 && !dumpWatchers && !dumpWatchers) {
                 boolean printedHeader = false;
                 for (int o=0; o<mAudioRestrictions.size(); o++) {
                     final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
@@ -3800,7 +3895,7 @@
                 final SparseIntArray opModes = uidState.opModes;
                 final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
 
-                if (dumpWatchers) {
+                if (dumpWatchers || dumpHistory) {
                     continue;
                 }
                 if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
@@ -3847,10 +3942,10 @@
 
                 pw.print("  Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
                 pw.print("    state=");
-                pw.println(UID_STATE_NAMES[uidState.state]);
+                pw.println(AppOpsManager.getUidStateName(uidState.state));
                 if (uidState.state != uidState.pendingState) {
                     pw.print("    pendingState=");
-                    pw.println(UID_STATE_NAMES[uidState.pendingState]);
+                    pw.println(AppOpsManager.getUidStateName(uidState.pendingState));
                 }
                 if (uidState.pendingStateCommitTime != 0) {
                     pw.print("    pendingStateCommitTime=");
@@ -3906,7 +4001,8 @@
                     boolean printedPackage = false;
                     for (int j=0; j<ops.size(); j++) {
                         final Op op = ops.valueAt(j);
-                        if (dumpOp >= 0 && dumpOp != op.op) {
+                        final int opCode = op.op;
+                        if (dumpOp >= 0 && dumpOp != opCode) {
                             continue;
                         }
                         if (dumpMode >= 0 && dumpMode != op.mode) {
@@ -3916,32 +4012,23 @@
                             pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
                             printedPackage = true;
                         }
-                        pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
+                        pw.print("      "); pw.print(AppOpsManager.opToName(opCode));
                         pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode));
-                        final int switchOp = AppOpsManager.opToSwitch(op.op);
-                        if (switchOp != op.op) {
+                        final int switchOp = AppOpsManager.opToSwitch(opCode);
+                        if (switchOp != opCode) {
                             pw.print(" / switch ");
                             pw.print(AppOpsManager.opToName(switchOp));
                             final Op switchObj = ops.get(switchOp);
-                            int mode = switchObj != null
-                                    ? switchObj.mode : AppOpsManager.opToDefaultMode(switchOp);
+                            int mode = switchObj != null ? switchObj.mode
+                                    : AppOpsManager.opToDefaultMode(switchOp);
                             pw.print("="); pw.print(AppOpsManager.modeToName(mode));
                         }
                         pw.println("): ");
-                        dumpTimesLocked(pw,
-                                "          Access: ",
-                                "                  ", op.time, now, sdf, date);
-                        dumpTimesLocked(pw,
-                                "          Reject: ",
-                                "                  ", op.rejectTime, now, sdf, date);
-                        if (op.duration == -1) {
+                        dumpStatesLocked(pw, op, now, sdf, date, "          ");
+                        if (op.running) {
                             pw.print("          Running start at: ");
                             TimeUtils.formatDuration(nowElapsed-op.startRealtime, pw);
                             pw.println();
-                        } else if (op.duration != 0) {
-                            pw.print("          duration=");
-                            TimeUtils.formatDuration(op.duration, pw);
-                            pw.println();
                         }
                         if (op.startNesting != 0) {
                             pw.print("          startNesting=");
@@ -3960,7 +4047,7 @@
                 ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
                 boolean printedTokenHeader = false;
 
-                if (dumpMode >= 0 || dumpWatchers) {
+                if (dumpMode >= 0 || dumpWatchers || dumpHistory) {
                     continue;
                 }
 
@@ -4044,7 +4131,9 @@
         }
 
         // Must not hold the appops lock
-        mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpOp);
+        if (dumpHistory && !dumpWatchers) {
+            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpOp);
+        }
     }
 
     private static final class Restriction {
@@ -4158,7 +4247,7 @@
                 final ClientState client = mClients.valueAt(i);
                 for (int j = client.mStartedOps.size() - 1; j >= 0; j--) {
                     final Op op = client.mStartedOps.get(j);
-                    if (op.op == code && op.uid == uid) return true;
+                    if (op.op == code && op.uidState.uid == uid) return true;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 4485a54..280bc02 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -23,6 +23,7 @@
 import android.app.AppOpsManager.HistoricalOps;
 import android.app.AppOpsManager.HistoricalPackageOps;
 import android.app.AppOpsManager.HistoricalUidOps;
+import android.app.AppOpsManager.OpFlags;
 import android.app.AppOpsManager.UidState;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
@@ -37,6 +38,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.Xml;
@@ -297,20 +299,20 @@
         }
     }
 
-    @Nullable void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
+    void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
             @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
-            @NonNull RemoteCallback callback) {
+            @OpFlags int flags, @NonNull RemoteCallback callback) {
         final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
         mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
-                beginTimeMillis, endTimeMillis);
+                beginTimeMillis, endTimeMillis, flags);
         final Bundle payload = new Bundle();
         payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
         callback.sendResult(payload);
     }
 
-    @Nullable void getHistoricalOps(int uid, @NonNull String packageName,
+    void getHistoricalOps(int uid, @NonNull String packageName,
             @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
-            @NonNull RemoteCallback callback) {
+            @OpFlags int flags, @NonNull RemoteCallback callback) {
         final long currentTimeMillis = System.currentTimeMillis();
         if (endTimeMillis == Long.MAX_VALUE) {
             endTimeMillis = currentTimeMillis;
@@ -326,6 +328,8 @@
         synchronized (mOnDiskLock) {
             final List<HistoricalOps> pendingWrites;
             final HistoricalOps currentOps;
+            boolean collectOpsFromDisk;
+
             synchronized (mInMemoryLock) {
                 currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis);
                 if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis()
@@ -338,10 +342,11 @@
                 }
                 pendingWrites = new ArrayList<>(mPendingWrites);
                 mPendingWrites.clear();
+                collectOpsFromDisk = inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis();
             }
 
             // If the query was only for in-memory state - done.
-            if (inMemoryAdjEndTimeMillis > currentOps.getEndTimeMillis()) {
+            if (collectOpsFromDisk) {
                 // If there is a write in flight we need to force it now
                 persistPendingHistory(pendingWrites);
                 // Collect persisted state.
@@ -352,7 +357,7 @@
                 final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
                         - onDiskAndInMemoryOffsetMillis, 0);
                 mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
-                        onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis);
+                        onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
             }
 
             // Rebase the result time to be since epoch.
@@ -366,32 +371,31 @@
     }
 
     void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
-            @UidState int uidState) {
+            @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
-                        .increaseAccessCount(op, uid, packageName, uidState, 1);
-
+                        .increaseAccessCount(op, uid, packageName, uidState, flags, 1);
             }
         }
     }
 
     void incrementOpRejected(int op, int uid, @NonNull String packageName,
-            @UidState int uidState) {
+            @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
-                        .increaseRejectCount(op, uid, packageName, uidState, 1);
+                        .increaseRejectCount(op, uid, packageName, uidState, flags, 1);
             }
         }
     }
 
     void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
-            @UidState int uidState, long increment) {
+            @UidState int uidState, @OpFlags int flags, long increment) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
-                        .increaseAccessDuration(op, uid, packageName, uidState, increment);
+                        .increaseAccessDuration(op, uid, packageName, uidState, flags, increment);
             }
         }
     }
@@ -593,20 +597,20 @@
         private static final String TAG_HISTORY = "history";
         private static final String TAG_OPS = "ops";
         private static final String TAG_UID = "uid";
-        private static final String TAG_PACKAGE = "package";
+        private static final String TAG_PACKAGE = "pkg";
         private static final String TAG_OP = "op";
-        private static final String TAG_STATE = "state";
+        private static final String TAG_STATE = "st";
 
-        private static final String ATTR_VERSION = "version";
-        private static final String ATTR_NAME = "name";
-        private static final String ATTR_ACCESS_COUNT = "accessCount";
-        private static final String ATTR_REJECT_COUNT = "rejectCount";
-        private static final String ATTR_ACCESS_DURATION = "accessDuration";
-        private static final String ATTR_BEGIN_TIME = "beginTime";
-        private static final String ATTR_END_TIME = "endTime";
-        private static final String ATTR_OVERFLOW = "overflow";
+        private static final String ATTR_VERSION = "ver";
+        private static final String ATTR_NAME = "na";
+        private static final String ATTR_ACCESS_COUNT = "ac";
+        private static final String ATTR_REJECT_COUNT = "rc";
+        private static final String ATTR_ACCESS_DURATION = "du";
+        private static final String ATTR_BEGIN_TIME = "beg";
+        private static final String ATTR_END_TIME = "end";
+        private static final String ATTR_OVERFLOW = "ov";
 
-        private static final int CURRENT_VERSION = 1;
+        private static final int CURRENT_VERSION = 2;
 
         private final long mBaseSnapshotInterval;
         private final long mIntervalCompressionMultiplier;
@@ -657,7 +661,8 @@
         @Nullable List<HistoricalOps> readHistoryRawDLocked() {
             return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/,
                     null /*filterPackageName*/, null /*filterOpNames*/,
-                    0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/);
+                    0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
+                    AppOpsManager.OP_FLAGS_ALL);
         }
 
         @Nullable List<HistoricalOps> readHistoryDLocked() {
@@ -697,9 +702,10 @@
 
         private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps,
                 int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
-                long filterBeingMillis, long filterEndMillis) {
+                long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) {
             final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid,
-                    filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis);
+                    filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis,
+                    filterFlags);
             if (readOps != null) {
                 final int readCount = readOps.size();
                 for (int i = 0; i < readCount; i++) {
@@ -711,7 +717,7 @@
 
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(
                 int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
-                long filterBeginTimeMillis, long filterEndTimeMillis) {
+                long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) {
             File baseDir = null;
             try {
                 baseDir = mHistoricalAppOpsDir.startRead();
@@ -724,8 +730,8 @@
                 final long[] globalContentOffsetMillis = {0};
                 final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked(
                         baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
-                        filterEndTimeMillis, globalContentOffsetMillis, null /*outOps*/,
-                        0 /*depth*/, historyFiles);
+                        filterEndTimeMillis, filterFlags, globalContentOffsetMillis,
+                        null /*outOps*/, 0 /*depth*/, historyFiles);
                 if (DEBUG) {
                     filesInvariant.stopTracking(baseDir);
                 }
@@ -741,7 +747,8 @@
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked(
                 @NonNull File baseDir, int filterUid, @NonNull String filterPackageName,
                 @Nullable String[] filterOpNames, long filterBeginTimeMillis,
-                long filterEndTimeMillis, @NonNull long[] globalContentOffsetMillis,
+                long filterEndTimeMillis, @OpFlags int filterFlags,
+                @NonNull long[] globalContentOffsetMillis,
                 @Nullable LinkedList<HistoricalOps> outOps, int depth,
                 @NonNull Set<String> historyFiles)
                 throws IOException, XmlPullParserException {
@@ -757,7 +764,7 @@
             final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir,
                     previousIntervalEndMillis, currentIntervalEndMillis, filterUid,
                     filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis,
-                    globalContentOffsetMillis, depth, historyFiles);
+                    filterFlags, globalContentOffsetMillis, depth, historyFiles);
 
             // Empty is a special signal to stop diving
             if (readOps != null && readOps.isEmpty()) {
@@ -767,7 +774,7 @@
             // Collect older historical data from subsequent levels
             outOps = collectHistoricalOpsRecursiveDLocked(
                     baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
-                    filterEndTimeMillis, globalContentOffsetMillis, outOps, depth + 1,
+                    filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1,
                     historyFiles);
 
             // Make older historical data relative to the current historical level
@@ -836,22 +843,24 @@
                     previousIntervalEndMillis, currentIntervalEndMillis,
                     Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/,
                     null /*filterOpNames*/, Long.MIN_VALUE /*filterBeginTimeMillis*/,
-                    Long.MAX_VALUE /*filterEndTimeMillis*/, null, depth,
-                    null /*historyFiles*/);
+                    Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL,
+                    null, depth, null /*historyFiles*/);
 
             if (DEBUG) {
                 enforceOpsWellFormed(existingOps);
             }
 
             // Offset existing ops to account for elapsed time
-            final int existingOpCount = existingOps.size();
-            if (existingOpCount > 0) {
-                // Compute elapsed time
-                final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
+            if (existingOps != null) {
+                final int existingOpCount = existingOps.size();
+                if (existingOpCount > 0) {
+                    // Compute elapsed time
+                    final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
                         .getEndTimeMillis();
-                for (int i = 0; i < existingOpCount; i++) {
-                    final HistoricalOps existingOp = existingOps.get(i);
-                    existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
+                    for (int i = 0; i < existingOpCount; i++) {
+                        final HistoricalOps existingOp = existingOps.get(i);
+                        existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
+                    }
                 }
             }
 
@@ -864,9 +873,10 @@
             // Consolidate passed ops at the current slot duration ensuring each snapshot is
             // full. To achieve this we put all passed and existing ops in a list and will
             // merge them to ensure each represents a snapshot at the current granularity.
-            final List<HistoricalOps> allOps = new LinkedList<>();
-            allOps.addAll(passedOps);
-            allOps.addAll(existingOps);
+            final List<HistoricalOps> allOps = new LinkedList<>(passedOps);
+            if (existingOps != null) {
+                allOps.addAll(existingOps);
+            }
 
             if (DEBUG) {
                 enforceOpsWellFormed(allOps);
@@ -944,10 +954,10 @@
                     overflowedOps, oldFileNames, depth + 1);
         }
 
-        private @NonNull List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
-                long intervalBeginMillis, long intervalEndMillis, int filterUid,
-                @Nullable String filterPackageName, @Nullable String[] filterOpNames,
-                long filterBeginTimeMillis, long filterEndTimeMillis,
+        private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
+                long intervalBeginMillis, long intervalEndMillis,
+                int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis, int depth,
                 @NonNull Set<String> historyFiles)
                 throws IOException, XmlPullParserException {
@@ -973,12 +983,13 @@
                 }
             }
             return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterOpNames,
-                    filterBeginTimeMillis, filterEndTimeMillis, cumulativeOverflowMillis);
+                    filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+                    cumulativeOverflowMillis);
         }
 
         private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
                 int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
-                long filterBeginTimeMillis, long filterEndTimeMillis,
+                long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
             if (DEBUG) {
@@ -989,13 +1000,22 @@
                 final XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(stream, StandardCharsets.UTF_8.name());
                 XmlUtils.beginDocument(parser, TAG_HISTORY);
+
+                // We haven't released version 1 and have more detailed
+                // accounting - just nuke the current state
+                final int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
+                if (CURRENT_VERSION == 2 && version < CURRENT_VERSION) {
+                    throw new IllegalStateException("Dropping unsupported history "
+                            + "version 1 for file:" + file);
+                }
+
                 final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0);
                 final int depth = parser.getDepth();
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_OPS.equals(parser.getName())) {
                         final HistoricalOps ops = readeHistoricalOpsDLocked(parser,
                                 filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
-                                filterEndTimeMillis, cumulativeOverflowMillis);
+                                filterEndTimeMillis, filterFlags, cumulativeOverflowMillis);
                         if (ops == null) {
                             continue;
                         }
@@ -1029,7 +1049,8 @@
         private @Nullable HistoricalOps readeHistoricalOpsDLocked(
                 @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
                 @Nullable String[] filterOpNames, long filterBeginTimeMillis,
-                long filterEndTimeMillis, @Nullable long[] cumulativeOverflowMillis)
+                long filterEndTimeMillis, @OpFlags int filterFlags,
+                @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
             final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0)
                     + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
@@ -1045,6 +1066,10 @@
             }
             final long filteredBeginTimeMillis = Math.max(beginTimeMillis, filterBeginTimeMillis);
             final long filteredEndTimeMillis = Math.min(endTimeMillis, filterEndTimeMillis);
+            // // Keep reading as subsequent records may start matching
+            // if (filteredEndTimeMillis - filterBeginTimeMillis <= 0) {
+            //     return null;
+            // }
             final double filterScale = (double) (filteredEndTimeMillis - filteredBeginTimeMillis)
                     / (double) (endTimeMillis - beginTimeMillis);
             HistoricalOps ops = null;
@@ -1052,7 +1077,7 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_UID.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser,
-                            filterUid, filterPackageName, filterOpNames, filterScale);
+                            filterUid, filterPackageName, filterOpNames, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1067,7 +1092,8 @@
         private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
                 @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
                 @Nullable String filterPackageName, @Nullable String[] filterOpNames,
-                double filterScale) throws IOException, XmlPullParserException {
+                @OpFlags int filterFlags, double filterScale)
+                throws IOException, XmlPullParserException {
             final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME);
             if (filterUid != Process.INVALID_UID && filterUid != uid) {
                 XmlUtils.skipCurrentTag(parser);
@@ -1077,7 +1103,8 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_PACKAGE.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops,
-                            uid, parser, filterPackageName, filterOpNames, filterScale);
+                            uid, parser, filterPackageName, filterOpNames, filterFlags,
+                            filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1089,7 +1116,8 @@
         private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
                 @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
                 @Nullable String filterPackageName, @Nullable String[] filterOpNames,
-                double filterScale) throws IOException, XmlPullParserException {
+                @OpFlags int filterFlags, double filterScale)
+                throws IOException, XmlPullParserException {
             final String packageName = XmlUtils.readStringAttribute(parser, ATTR_NAME);
             if (filterPackageName != null && !filterPackageName.equals(packageName)) {
                 XmlUtils.skipCurrentTag(parser);
@@ -1099,7 +1127,7 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_OP.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid,
-                            packageName, parser, filterOpNames, filterScale);
+                            packageName, parser, filterOpNames, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1110,7 +1138,7 @@
 
         private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
                 int uid, String packageName, @NonNull XmlPullParser parser,
-                @Nullable String[] filterOpNames, double filterScale)
+                @Nullable String[] filterOpNames, @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
             final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME);
             if (filterOpNames != null && !ArrayUtils.contains(filterOpNames,
@@ -1121,8 +1149,8 @@
             final int depth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_STATE.equals(parser.getName())) {
-                    final HistoricalOps returnedOps = readUidStateDLocked(ops, uid,
-                            packageName, op, parser, filterScale);
+                    final HistoricalOps returnedOps = readStateDLocked(ops, uid,
+                            packageName, op, parser, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1131,10 +1159,15 @@
             return ops;
         }
 
-        private @Nullable HistoricalOps readUidStateDLocked(@Nullable HistoricalOps ops,
+        private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
                 int uid, String packageName, int op, @NonNull XmlPullParser parser,
-                double filterScale) throws IOException {
-            final int uidState = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+                @OpFlags int filterFlags, double filterScale) throws IOException {
+            final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
+            final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags;
+            if (flags == 0) {
+                return null;
+            }
+            final int uidState = AppOpsManager.extractUidStateFromKey(key);
             long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0);
             if (accessCount > 0) {
                 if (!Double.isNaN(filterScale)) {
@@ -1144,7 +1177,7 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseAccessCount(op, uid, packageName, uidState, accessCount);
+                ops.increaseAccessCount(op, uid, packageName, uidState, flags, accessCount);
             }
             long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
             if (rejectCount > 0) {
@@ -1155,7 +1188,7 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseRejectCount(op, uid, packageName, uidState, rejectCount);
+                ops.increaseRejectCount(op, uid, packageName, uidState, flags, rejectCount);
             }
             long accessDuration =  XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
             if (accessDuration > 0) {
@@ -1166,7 +1199,7 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseAccessDuration(op, uid, packageName, uidState, accessDuration);
+                ops.increaseAccessDuration(op, uid, packageName, uidState, flags, accessDuration);
             }
             return ops;
         }
@@ -1241,24 +1274,34 @@
 
         private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
                 @NonNull XmlSerializer serializer) throws IOException {
+            final LongSparseArray keys = op.collectKeys();
+            if (keys == null || keys.size() <= 0) {
+                return;
+            }
             serializer.startTag(null, TAG_OP);
             serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode()));
-            for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) {
-                writeUidStateOnLocked(op, uidState, serializer);
+            final int keyCount = keys.size();
+            for (int i = 0; i < keyCount; i++) {
+                writeStateOnLocked(op, keys.keyAt(i), serializer);
             }
             serializer.endTag(null, TAG_OP);
         }
 
-        private void writeUidStateOnLocked(@NonNull HistoricalOp op, @UidState int uidState,
+        private void writeStateOnLocked(@NonNull HistoricalOp op, long key,
                 @NonNull XmlSerializer serializer) throws IOException {
-            final long accessCount = op.getAccessCount(uidState);
-            final long rejectCount = op.getRejectCount(uidState);
-            final long accessDuration = op.getAccessDuration(uidState);
-            if (accessCount == 0 && rejectCount == 0 && accessDuration == 0) {
+            final int uidState = AppOpsManager.extractUidStateFromKey(key);
+            final int flags = AppOpsManager.extractFlagsFromKey(key);
+
+            final long accessCount = op.getAccessCount(uidState, uidState, flags);
+            final long rejectCount = op.getRejectCount(uidState, uidState, flags);
+            final long accessDuration = op.getAccessDuration(uidState, uidState, flags);
+
+            if (accessCount <= 0 && rejectCount <= 0 && accessDuration <= 0) {
                 return;
             }
+
             serializer.startTag(null, TAG_STATE);
-            serializer.attribute(null, ATTR_NAME, Integer.toString(uidState));
+            serializer.attribute(null, ATTR_NAME, Long.toString(key));
             if (accessCount > 0) {
                 serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount));
             }
@@ -1532,24 +1575,29 @@
             mWriter.print(mEntryPrefix);
             mWriter.print(AppOpsManager.opToName(ops.getOpCode()));
             mWriter.println(":");
-            for (int uidState = 0; uidState < AppOpsManager._NUM_UID_STATE; uidState++) {
+            final LongSparseArray keys = ops.collectKeys();
+            final int keyCount = keys.size();
+            for (int i = 0; i < keyCount; i++) {
+                final long key = keys.keyAt(i);
+                final int uidState = AppOpsManager.extractUidStateFromKey(key);
+                final int flags = AppOpsManager.extractFlagsFromKey(key);
                 boolean printedUidState = false;
-                final long accessCount = ops.getAccessCount(uidState);
+                final long accessCount = ops.getAccessCount(uidState, uidState, flags);
                 if (accessCount > 0) {
                     if (!printedUidState) {
                         mWriter.print(mUidStatePrefix);
-                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(AppOpsManager.keyToString(key));
                         mWriter.print(" = ");
                         printedUidState = true;
                     }
                     mWriter.print("access=");
                     mWriter.print(accessCount);
                 }
-                final long rejectCount = ops.getRejectCount(uidState);
+                final long rejectCount = ops.getRejectCount(uidState, uidState, flags);
                 if (rejectCount > 0) {
                     if (!printedUidState) {
                         mWriter.print(mUidStatePrefix);
-                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(AppOpsManager.keyToString(key));
                         mWriter.print(" = ");
                         printedUidState = true;
                     } else {
@@ -1558,11 +1606,11 @@
                     mWriter.print("reject=");
                     mWriter.print(rejectCount);
                 }
-                final long accessDuration = ops.getAccessDuration(uidState);
+                final long accessDuration = ops.getAccessDuration(uidState, uidState, flags);
                 if (accessDuration > 0) {
                     if (!printedUidState) {
                         mWriter.print(mUidStatePrefix);
-                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(AppOpsManager.keyToString(key));
                         mWriter.print(" = ");
                         printedUidState = true;
                     } else {
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
new file mode 100644
index 0000000..f2e2782
--- /dev/null
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsAppOpsTestCases"
+        },
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.appop.AppOpsUpgradeTest"
+                },
+                {
+                    "include-filter": "com.android.server.appop.AppOpsServiceTest"
+                },
+                {
+                    "include-filter": "com.android.server.appop.AppOpsActiveWatcherTest"
+                }
+            ]
+        }
+    ]
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9af57da..451fd66 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -201,51 +201,72 @@
         }
     }
 
-    /*package*/ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+    private static final class BtDeviceConnectionInfo {
+        final @NonNull BluetoothDevice mDevice;
+        final @AudioService.BtProfileConnectionState int mState;
+        final int mProfile;
+        final boolean mSupprNoisy;
+        final int mVolume;
+
+        BtDeviceConnectionInfo(@NonNull BluetoothDevice device,
+                @AudioService.BtProfileConnectionState int state,
+                int profile, boolean suppressNoisyIntent, int vol) {
+            mDevice = device;
+            mState = state;
+            mProfile = profile;
+            mSupprNoisy = suppressNoisyIntent;
+            mVolume = vol;
+        }
+    }
+
+    /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             int profile, boolean suppressNoisyIntent, int a2dpVolume) {
-        AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
-                        // only querying address as this is the only readily available field
-                        // on the device
-                        + " addr=" + device.getAddress()
-                        + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
-                        + " vol=" + a2dpVolume)).printLog(TAG));
-        synchronized (mDeviceStateLock) {
-            if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
-                    new BtHelper.BluetoothA2dpDeviceInfo(device))) {
-                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
-                        "A2DP connection state ignored"));
-                return 0;
-            }
-            return mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
-                    device, state, profile, suppressNoisyIntent,
-                    AudioSystem.DEVICE_NONE, a2dpVolume);
-        }
+        final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
+                suppressNoisyIntent, a2dpVolume);
+
+        // TODO add a check to try to remove unprocessed messages for the same device (the old
+        //      check didn't work), and  make sure it doesn't conflict with config change message
+        sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
     }
 
     /*package*/ int handleBluetoothA2dpActiveDeviceChange(
             @NonNull BluetoothDevice device,
             @AudioService.BtProfileConnectionState int state, int profile,
             boolean suppressNoisyIntent, int a2dpVolume) {
+        // FIXME method was added by @a8439e2 but never used, and now conflicts with async behavior
+        //   of handleBluetoothA2dpDeviceConfigChange and
+        //   setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
         synchronized (mDeviceStateLock) {
             return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile,
                     suppressNoisyIntent, a2dpVolume);
         }
     }
 
-    /*package*/ int setBluetoothHearingAidDeviceConnectionState(
+    private static final class HearingAidDeviceConnectionInfo {
+        final @NonNull BluetoothDevice mDevice;
+        final @AudioService.BtProfileConnectionState int mState;
+        final boolean mSupprNoisy;
+        final int mMusicDevice;
+        final @NonNull String mEventSource;
+
+        HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device,
+                @AudioService.BtProfileConnectionState int state,
+                boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
+            mDevice = device;
+            mState = state;
+            mSupprNoisy = suppressNoisyIntent;
+            mMusicDevice = musicDevice;
+            mEventSource = eventSource;
+        }
+    }
+
+    /*package*/ void postBluetoothHearingAidDeviceConnectionState(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
-        AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                "setHearingAidDeviceConnectionState state=" + state
-                        + " addr=" + device.getAddress()
-                        + " supprNoisy=" + suppressNoisyIntent
-                        + " src=" + eventSource)).printLog(TAG));
-        synchronized (mDeviceStateLock) {
-            return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
-                    device, state, suppressNoisyIntent, musicDevice);
-        }
+        final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
+                device, state, suppressNoisyIntent, musicDevice, eventSource);
+        sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
     }
 
     // never called by system components
@@ -766,6 +787,35 @@
                         mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
                     }
                     break;
+                case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: {
+                    final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj;
+                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+                            "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent "
+                                    + " state=" + info.mState
+                                    // only querying address as this is the only readily available
+                                    // field on the device
+                                    + " addr=" + info.mDevice.getAddress()
+                                    + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
+                                    + " vol=" + info.mVolume)).printLog(TAG));
+                    synchronized (mDeviceStateLock) {
+                        mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
+                                info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
+                                AudioSystem.DEVICE_NONE, info.mVolume);
+                    }
+                } break;
+                case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
+                    final HearingAidDeviceConnectionInfo info =
+                            (HearingAidDeviceConnectionInfo) msg.obj;
+                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+                            "setHearingAidDeviceConnectionState state=" + info.mState
+                                    + " addr=" + info.mDevice.getAddress()
+                                    + " supprNoisy=" + info.mSupprNoisy
+                                    + " src=" + info.mEventSource)).printLog(TAG));
+                    synchronized (mDeviceStateLock) {
+                        mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
+                                info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+                    }
+                } break;
                 default:
                     Log.wtf(TAG, "Invalid message " + msg.what);
             }
@@ -809,6 +859,10 @@
     private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24;
     private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25;
     private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26;
+    // process external command to (dis)connect an A2DP device
+    private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 27;
+    // process external command to (dis)connect a hearing aid device
+    private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28;
 
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -821,6 +875,8 @@
             case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
             case MSG_TOGGLE_HDMI:
             case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
+            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
+            case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
                 return true;
             default:
                 return false;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 37f0496..41a3c98 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -578,7 +578,7 @@
         return mCurAudioRoutes;
     }
 
-    /*package*/ int setBluetoothA2dpDeviceConnectionState(
+    /*package*/ void setBluetoothA2dpDeviceConnectionState(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) {
         int delay;
@@ -614,46 +614,50 @@
                         delay);
             }
         }
-        return delay;
     }
 
     /*package*/ int handleBluetoothA2dpActiveDeviceChange(
             @NonNull BluetoothDevice device,
             @AudioService.BtProfileConnectionState int state, int profile,
             boolean suppressNoisyIntent, int a2dpVolume) {
-        if (state == BluetoothProfile.STATE_DISCONNECTED) {
-            return setBluetoothA2dpDeviceConnectionState(device, state, profile,
-                    suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
-        }
-        // state == BluetoothProfile.STATE_CONNECTED
-        synchronized (mConnectedDevices) {
-            for (int i = 0; i < mConnectedDevices.size(); i++) {
-                final DeviceInfo deviceInfo = mConnectedDevices.valueAt(i);
-                if (deviceInfo.mDeviceType != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
-                    continue;
-                }
-                // If A2DP device exists, this is either an active device change or
-                // device config change
-                final String existingDevicekey = mConnectedDevices.keyAt(i);
-                final String deviceName = device.getName();
-                final String address = device.getAddress();
-                final String newDeviceKey = DeviceInfo.makeDeviceListKey(
-                        AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
-                int a2dpCodec = mDeviceBroker.getA2dpCodec(device);
-                // Device not equal to existing device, active device change
-                if (!TextUtils.equals(existingDevicekey, newDeviceKey)) {
-                    mConnectedDevices.remove(existingDevicekey);
-                    mConnectedDevices.put(newDeviceKey, new DeviceInfo(
-                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, deviceName,
-                            address, a2dpCodec));
-                    mDeviceBroker.postA2dpActiveDeviceChange(
-                            new BtHelper.BluetoothA2dpDeviceInfo(
-                                    device, a2dpVolume, a2dpCodec));
-                    return 0;
-                } else {
-                    // Device config change for existing device
-                    mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device);
-                    return 0;
+        // method was added by QC but never used, and now conflicts with async behavior of
+        // handleBluetoothA2dpDeviceConfigChange and
+        // setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
+        if (false) {
+            if (state == BluetoothProfile.STATE_DISCONNECTED) {
+                setBluetoothA2dpDeviceConnectionState(device, state, profile,
+                        suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
+            }
+            // state == BluetoothProfile.STATE_CONNECTED
+            synchronized (mConnectedDevices) {
+                for (int i = 0; i < mConnectedDevices.size(); i++) {
+                    final DeviceInfo deviceInfo = mConnectedDevices.valueAt(i);
+                    if (deviceInfo.mDeviceType != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+                        continue;
+                    }
+                    // If A2DP device exists, this is either an active device change or
+                    // device config change
+                    final String existingDevicekey = mConnectedDevices.keyAt(i);
+                    final String deviceName = device.getName();
+                    final String address = device.getAddress();
+                    final String newDeviceKey = DeviceInfo.makeDeviceListKey(
+                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
+                    int a2dpCodec = mDeviceBroker.getA2dpCodec(device);
+                    // Device not equal to existing device, active device change
+                    if (!TextUtils.equals(existingDevicekey, newDeviceKey)) {
+                        mConnectedDevices.remove(existingDevicekey);
+                        mConnectedDevices.put(newDeviceKey, new DeviceInfo(
+                                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, deviceName,
+                                address, a2dpCodec));
+                        mDeviceBroker.postA2dpActiveDeviceChange(
+                                new BtHelper.BluetoothA2dpDeviceInfo(
+                                        device, a2dpVolume, a2dpCodec));
+                        return 0;
+                    } else {
+                        // Device config change for existing device
+                        mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device);
+                        return 0;
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6bd412b..91d19de 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4041,7 +4041,10 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface BtProfileConnectionState {}
 
-    public int setBluetoothHearingAidDeviceConnectionState(
+    /**
+     * See AudioManager.setBluetoothHearingAidDeviceConnectionState()
+     */
+    public void setBluetoothHearingAidDeviceConnectionState(
             @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
             boolean suppressNoisyIntent, int musicDevice)
     {
@@ -4053,14 +4056,14 @@
             throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
                     + " (dis)connection, got " + state);
         }
-        return mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
+        mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
                 device, state, suppressNoisyIntent, musicDevice, "AudioService");
     }
 
     /**
      * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
      */
-    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             int profile, boolean suppressNoisyIntent, int a2dpVolume) {
         if (device == null) {
@@ -4071,7 +4074,7 @@
             throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
                     + " (dis)connection, got " + state);
         }
-        return mDeviceBroker.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
+        mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
                 profile, suppressNoisyIntent, a2dpVolume);
     }
 
@@ -4094,6 +4097,9 @@
     public int handleBluetoothA2dpActiveDeviceChange(
             BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
             int a2dpVolume) {
+        // FIXME method was added by @a8439e2 but never used, and now conflicts with async behavior
+        //   of handleBluetoothA2dpDeviceConfigChange and
+        //   setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
         if (device == null) {
             throw new IllegalArgumentException("Illegal null device");
         }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 58c1882..04073cb 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -426,7 +426,7 @@
         final BluetoothDevice btDevice = deviceList.get(0);
         final @BluetoothProfile.BtProfileState int state =
                 mHearingAid.getConnectionState(btDevice);
-        mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
+        mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
                 btDevice, state,
                 /*suppressNoisyIntent*/ false,
                 /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index e9ae516..9882f6c 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -138,9 +138,19 @@
     }
 
     /**
-     * Sets the mode, if supported.
+     * Sets the display modes the system is allowed to switch between, roughly ordered by
+     * preference.
+     *
+     * Not all display devices will automatically switch between modes, so it's important that the
+     * most-desired modes are at the beginning of the allowed array.
      */
-    public void requestDisplayModesLocked(int colorMode, int modeId) {
+    public void setAllowedDisplayModesLocked(int[] modes) {
+    }
+
+    /**
+     * Sets the requested color mode.
+     */
+    public void setRequestedColorModeLocked(int colorMode) {
     }
 
     public void onOverlayChangedLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6c00da2..32f34b8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -167,13 +167,13 @@
     private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
     private static final int MSG_REQUEST_TRAVERSAL = 4;
     private static final int MSG_UPDATE_VIEWPORT = 5;
-    private static final int MSG_REGISTER_BRIGHTNESS_TRACKER = 6;
-    private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 7;
+    private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
 
     private final Context mContext;
     private final DisplayManagerHandler mHandler;
     private final Handler mUiHandler;
     private final DisplayAdapterListener mDisplayAdapterListener;
+    private final DisplayModeDirector mDisplayModeDirector;
     private WindowManagerInternal mWindowManagerInternal;
     private InputManagerInternal mInputManagerInternal;
     private IMediaProjectionManager mProjectionService;
@@ -310,6 +310,7 @@
         mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
         mUiHandler = UiThread.getHandler();
         mDisplayAdapterListener = new DisplayAdapterListener();
+        mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
         Resources resources = mContext.getResources();
         mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
@@ -322,7 +323,7 @@
         mMinimumBrightnessCurve = new Curve(lux, nits);
         mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
 
-        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        PowerManager pm = mContext.getSystemService(PowerManager.class);
         mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
         mCurrentUserId = UserHandle.USER_SYSTEM;
         ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
@@ -347,9 +348,9 @@
         // adapter is up so that we have it's configuration. We could load it lazily, but since
         // we're going to have to read it in eventually we may as well do it here rather than after
         // we've waited for the display to register itself with us.
-		synchronized(mSyncRoot) {
-			mPersistentDataStore.loadIfNeeded();
-			loadStableDisplayValuesLocked();
+        synchronized (mSyncRoot) {
+            mPersistentDataStore.loadIfNeeded();
+            loadStableDisplayValuesLocked();
         }
         mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
 
@@ -417,8 +418,10 @@
             mOnlyCore = onlyCore;
         }
 
+        mDisplayModeDirector.setListener(new AllowedDisplayModeObserver());
+        mDisplayModeDirector.start();
+
         mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
-        mHandler.sendEmptyMessage(MSG_REGISTER_BRIGHTNESS_TRACKER);
     }
 
     @VisibleForTesting
@@ -1194,13 +1197,8 @@
                 requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
                         requestedRefreshRate);
             }
-            if (display.getRequestedModeIdLocked() != requestedModeId) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Display " + displayId + " switching to mode " + requestedModeId);
-                }
-                display.setRequestedModeIdLocked(requestedModeId);
-                scheduleTraversalLocked(inTraversal);
-            }
+            mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
+                    displayId, requestedModeId);
         }
     }
 
@@ -1319,6 +1317,28 @@
         return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
     }
 
+    private void onAllowedDisplayModesChangedInternal() {
+        boolean changed = false;
+        synchronized (mSyncRoot) {
+            final int count = mLogicalDisplays.size();
+            for (int i = 0; i < count; i++) {
+                LogicalDisplay display = mLogicalDisplays.valueAt(i);
+                int displayId = mLogicalDisplays.keyAt(i);
+                int[] allowedModes = mDisplayModeDirector.getAllowedModes(displayId);
+                // Note that order is important here since not all display devices are capable of
+                // automatically switching, so we do actually want to check for equality and not
+                // just equivalent contents (regardless of order).
+                if (!Arrays.equals(allowedModes, display.getAllowedDisplayModesLocked())) {
+                    display.setAllowedDisplayModesLocked(allowedModes);
+                    changed = true;
+                }
+            }
+            if (changed) {
+                scheduleTraversalLocked(false);
+            }
+        }
+    }
+
     private void clearViewportsLocked() {
         mViewports.clear();
     }
@@ -1518,6 +1538,9 @@
                 display.dumpLocked(ipw);
             }
 
+            pw.println();
+            mDisplayModeDirector.dump(pw);
+
             final int callbackCount = mCallbacks.size();
             pw.println();
             pw.println("Callbacks: size=" + callbackCount);
@@ -2431,4 +2454,10 @@
         }
 
     }
+
+    class AllowedDisplayModeObserver implements DisplayModeDirector.Listener {
+        public void onAllowedDisplayModesChanged() {
+            onAllowedDisplayModesChangedInternal();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
new file mode 100644
index 0000000..af4db07
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -0,0 +1,685 @@
+/*
+ * 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.display;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.R;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * The DisplayModeDirector is responsible for determining what modes are allowed to be
+ * automatically picked by the system based on system-wide and display-specific configuration.
+ */
+public class DisplayModeDirector {
+    private static final String TAG = "DisplayModeDirector";
+    private static final boolean DEBUG = false;
+
+    private static final int MSG_ALLOWED_MODES_CHANGED = 1;
+
+    // Special ID used to indicate that given vote is to be applied globally, rather than to a
+    // specific display.
+    private static final int GLOBAL_ID = -1;
+
+    // What we consider to be the system's "default" refresh rate.
+    private static final float DEFAULT_REFRESH_RATE = 60f;
+
+    // The tolerance within which we consider something approximately equals.
+    private static final float EPSILON = 0.001f;
+
+    private final Object mLock = new Object();
+    private final Context mContext;
+
+    private final DisplayModeDirectorHandler mHandler;
+
+    // A map from the display ID to the collection of votes and their priority. The latter takes
+    // the form of another map from the priority to the vote itself so that each priority is
+    // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
+    private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
+    // A map from the display ID to the supported modes on that display.
+    private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
+    // A map from the display ID to the default mode of that display.
+    private final SparseArray<Display.Mode> mDefaultModeByDisplay;
+
+    private final AppRequestObserver mAppRequestObserver;
+    private final SettingsObserver mSettingsObserver;
+    private final DisplayObserver mDisplayObserver;
+
+
+    private Listener mListener;
+
+    public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
+        mContext = context;
+        mHandler = new DisplayModeDirectorHandler(handler.getLooper());
+        mVotesByDisplay = new SparseArray<>();
+        mSupportedModesByDisplay = new SparseArray<>();
+        mDefaultModeByDisplay =  new SparseArray<>();
+        mAppRequestObserver = new AppRequestObserver();
+        mSettingsObserver = new SettingsObserver(context, handler);
+        mDisplayObserver = new DisplayObserver(context, handler);
+    }
+
+    /**
+     * Tells the DisplayModeDirector to update allowed votes and begin observing relevant system
+     * state.
+     *
+     * This has to be deferred because the object may be constructed before the rest of the system
+     * is ready.
+     */
+    public void start() {
+        mSettingsObserver.observe();
+        mDisplayObserver.observe();
+        mSettingsObserver.observe();
+        synchronized (mLock) {
+            // We may have a listener already registered before the call to start, so go ahead and
+            // notify them to pick up our newly initialized state.
+            notifyAllowedModesChangedLocked();
+        }
+    }
+
+    /**
+     * Calculates the modes the system is allowed to freely switch between based on global and
+     * display-specific constraints.
+     *
+     * @param displayId The display to query for.
+     * @return The IDs of the modes the system is allowed to freely switch between.
+     */
+    @NonNull
+    public int[] getAllowedModes(int displayId) {
+        synchronized (mLock) {
+            SparseArray<Vote> votes = getVotesLocked(displayId);
+            Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+            Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
+            if (modes == null || defaultMode == null) {
+                Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
+                        + displayId + ")");
+                return new int[0];
+            }
+            return getAllowedModesLocked(votes, modes, defaultMode);
+        }
+    }
+
+    @NonNull
+    private SparseArray<Vote> getVotesLocked(int displayId) {
+        SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
+        final SparseArray<Vote> votes;
+        if (displayVotes != null) {
+            votes = displayVotes.clone();
+        } else {
+            votes = new SparseArray<>();
+        }
+
+        SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
+        if (globalVotes != null) {
+            for (int i = 0; i < globalVotes.size(); i++) {
+                int priority = globalVotes.keyAt(i);
+                if (votes.indexOfKey(priority) < 0) {
+                    votes.put(priority, globalVotes.valueAt(i));
+                }
+            }
+        }
+        return votes;
+    }
+
+    @NonNull
+    private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
+            @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
+        int lowestConsideredPriority = Vote.MIN_PRIORITY;
+        while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+            float minRefreshRate = 0f;
+            float maxRefreshRate = Float.POSITIVE_INFINITY;
+            int height = Vote.INVALID_SIZE;
+            int width = Vote.INVALID_SIZE;
+
+            for (int priority = Vote.MAX_PRIORITY;
+                    priority >= lowestConsideredPriority;
+                    priority--) {
+                Vote vote = votes.get(priority);
+                if (vote == null) {
+                    continue;
+                }
+                // For refresh rates, just use the tightest bounds of all the votes
+                minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
+                maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
+                // For display size, use only the first vote we come across (i.e. the highest
+                // priority vote that includes the width / height).
+                if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
+                        && vote.height > 0 && vote.width > 0) {
+                    width = vote.width;
+                    height = vote.height;
+                }
+            }
+
+            // If we don't have anything specifying the width / height of the display, just use the
+            // default width and height. We don't want these switching out from underneath us since
+            // it's a pretty disruptive behavior.
+            if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
+                width = defaultMode.getPhysicalWidth();
+                height = defaultMode.getPhysicalHeight();
+            }
+
+            int[] availableModes =
+                    filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
+            if (availableModes.length > 0) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
+                            + " with lowest priority considered "
+                            + Vote.priorityToString(lowestConsideredPriority)
+                            + " and constraints: "
+                            + "width=" + width
+                            + ", height=" + height
+                            + ", minRefreshRate=" + minRefreshRate
+                            + ", maxRefreshRate=" + maxRefreshRate);
+                }
+                return availableModes;
+            }
+
+            if (DEBUG) {
+                Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
+                        + Vote.priorityToString(lowestConsideredPriority)
+                        + " and with the following constraints: "
+                        + "width=" + width
+                        + ", height=" + height
+                        + ", minRefreshRate=" + minRefreshRate
+                        + ", maxRefreshRate=" + maxRefreshRate);
+            }
+            // If we haven't found anything with the current set of votes, drop the current lowest
+            // priority vote.
+            lowestConsideredPriority++;
+        }
+
+        // If we still haven't found anything that matches our current set of votes, just fall back
+        // to the default mode.
+        return new int[] { defaultMode.getModeId() };
+    }
+
+    private int[] filterModes(Display.Mode[] supportedModes,
+            int width, int height, float minRefreshRate, float maxRefreshRate) {
+        ArrayList<Display.Mode> availableModes = new ArrayList<>();
+        for (Display.Mode mode : supportedModes) {
+            if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
+                            + ": desiredWidth=" + width
+                            + ": desiredHeight=" + height
+                            + ": actualWidth=" + mode.getPhysicalWidth()
+                            + ": actualHeight=" + mode.getPhysicalHeight());
+                }
+                continue;
+            }
+            final float refreshRate = mode.getRefreshRate();
+            // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
+            // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
+            // comparison.
+            if (refreshRate < (minRefreshRate - EPSILON)
+                    || refreshRate > (maxRefreshRate + EPSILON)) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Discarding mode " + mode.getModeId()
+                            + ", outside refresh rate bounds"
+                            + ": minRefreshRate=" + minRefreshRate
+                            + ", maxRefreshRate=" + maxRefreshRate
+                            + ", modeRefreshRate=" + refreshRate);
+                }
+                continue;
+            }
+            availableModes.add(mode);
+        }
+        final int size = availableModes.size();
+        int[] availableModeIds = new int[size];
+        for (int i = 0; i < size; i++) {
+            availableModeIds[i] = availableModes.get(i).getModeId();
+        }
+        return availableModeIds;
+    }
+
+    /**
+     * Gets the observer responsible for application display mode requests.
+     */
+    @NonNull
+    public AppRequestObserver getAppRequestObserver() {
+        // We don't need to lock here because mAppRequestObserver is a final field, which is
+        // guaranteed to be visible on all threads after construction.
+        return mAppRequestObserver;
+    }
+
+    /**
+     * Sets the listener for changes to allowed display modes.
+     */
+    public void setListener(@Nullable Listener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+    }
+
+    /**
+     * Print the object's state and debug information into the given stream.
+     *
+     * @param pw The stream to dump information to.
+     */
+    public void dump(PrintWriter pw) {
+        pw.println("DisplayModeDirector");
+        synchronized (mLock) {
+            pw.println("  mSupportedModesByDisplay:");
+            for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
+                final int id = mSupportedModesByDisplay.keyAt(i);
+                final Display.Mode[] modes = mSupportedModesByDisplay.valueAt(i);
+                pw.println("    " + id + " -> " + Arrays.toString(modes));
+            }
+            pw.println("  mDefaultModeByDisplay:");
+            for (int i = 0; i < mDefaultModeByDisplay.size(); i++) {
+                final int id = mDefaultModeByDisplay.keyAt(i);
+                final Display.Mode mode = mDefaultModeByDisplay.valueAt(i);
+                pw.println("    " + id + " -> " + mode);
+            }
+            pw.println("  mVotesByDisplay:");
+            for (int i = 0; i < mVotesByDisplay.size(); i++) {
+                pw.println("    " + mVotesByDisplay.keyAt(i) + ":");
+                SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
+                for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
+                    Vote vote = votes.get(p);
+                    if (vote == null) {
+                        continue;
+                    }
+                    pw.println("      " + Vote.priorityToString(p) + " -> " + vote);
+                }
+            }
+            mSettingsObserver.dumpLocked(pw);
+            mAppRequestObserver.dumpLocked(pw);
+        }
+    }
+
+    private void updateVoteLocked(int priority, Vote vote) {
+        updateVoteLocked(GLOBAL_ID, priority, vote);
+    }
+
+    private void updateVoteLocked(int displayId, int priority, Vote vote) {
+        if (DEBUG) {
+            Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
+                    + ", priority=" + Vote.priorityToString(priority)
+                    + ", vote=" + vote + ")");
+        }
+        if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
+            Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
+                    + " priority=" + Vote.priorityToString(priority)
+                    + ", vote=" + vote, new Throwable());
+            return;
+        }
+        final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId);
+
+        Vote currentVote = votes.get(priority);
+        if (vote != null) {
+            votes.put(priority, vote);
+        } else {
+            votes.remove(priority);
+        }
+
+        if (votes.size() == 0) {
+            if (DEBUG) {
+                Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
+            }
+            mVotesByDisplay.remove(displayId);
+        }
+
+        notifyAllowedModesChangedLocked();
+    }
+
+    private void notifyAllowedModesChangedLocked() {
+        if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) {
+            // We need to post this to a handler to avoid calling out while holding the lock
+            // since we know there are things that both listen for changes as well as provide
+            // information. If we did call out while holding the lock, then there's no guaranteed
+            // lock order and we run the real of risk deadlock.
+            Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener);
+            msg.sendToTarget();
+        }
+    }
+
+    private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
+        int index = mVotesByDisplay.indexOfKey(displayId);
+        if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
+            return mVotesByDisplay.get(displayId);
+        } else {
+            SparseArray<Vote> votes = new SparseArray<>();
+            mVotesByDisplay.put(displayId, votes);
+            return votes;
+        }
+    }
+
+    /**
+     * Listens for changes to display mode coordination.
+     */
+    public interface Listener {
+        /**
+         * Called when the allowed display modes may have changed.
+         */
+        void onAllowedDisplayModesChanged();
+    }
+
+    private static final class DisplayModeDirectorHandler extends Handler {
+        DisplayModeDirectorHandler(Looper looper) {
+            super(looper, null, true /*async*/);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ALLOWED_MODES_CHANGED:
+                    Listener listener = (Listener) msg.obj;
+                    listener.onAllowedDisplayModesChanged();
+                    break;
+            }
+        }
+    }
+
+    private static final class Vote {
+        public static final int PRIORITY_USER_SETTING = 0;
+        // We split the app request into two priorities in case we can satisfy one desire without
+        // the other.
+        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 1;
+        public static final int PRIORITY_APP_REQUEST_SIZE = 2;
+        public static final int PRIORITY_LOW_POWER_MODE = 3;
+
+        // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
+        // appropriate, as well as priorityToString.
+
+        public static final int MIN_PRIORITY = PRIORITY_USER_SETTING;
+        public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
+
+        /**
+         * A value signifying an invalid width or height in a vote.
+         */
+        public static final int INVALID_SIZE = -1;
+
+        /**
+         * The requested width of the display in pixels, or INVALID_SIZE;
+         */
+        public final int width;
+        /**
+         * The requested height of the display in pixels, or INVALID_SIZE;
+         */
+        public final int height;
+
+        /**
+         * The lowest desired refresh rate.
+         */
+        public final float minRefreshRate;
+        /**
+         * The highest desired refresh rate.
+         */
+        public final float maxRefreshRate;
+
+        public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
+            return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
+        }
+
+        public static Vote forSize(int width, int height) {
+            return new Vote(width, height, 0f, Float.POSITIVE_INFINITY);
+        }
+
+        private Vote(int width, int height,
+                float minRefreshRate, float maxRefreshRate) {
+            this.width = width;
+            this.height = height;
+            this.minRefreshRate = minRefreshRate;
+            this.maxRefreshRate = maxRefreshRate;
+        }
+
+        public static String priorityToString(int priority) {
+            switch (priority) {
+                case PRIORITY_USER_SETTING:
+                    return "PRIORITY_USER_SETTING";
+                case PRIORITY_APP_REQUEST_REFRESH_RATE:
+                    return "PRIORITY_APP_REQUEST_REFRESH_RATE";
+                case PRIORITY_APP_REQUEST_SIZE:
+                    return "PRIORITY_APP_REQUEST_SIZE";
+                case PRIORITY_LOW_POWER_MODE:
+                    return "PRIORITY_LOW_POWER_MODE";
+                default:
+                    return Integer.toString(priority);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Vote{"
+                + "width=" + width
+                + ", height=" + height
+                + ", minRefreshRate=" + minRefreshRate
+                + ", maxRefreshRate=" + maxRefreshRate
+                + "}";
+        }
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        private final Uri mRefreshRateSetting =
+                Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+        private final Uri mLowPowerModeSetting =
+                Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
+
+        private final Context mContext;
+        private final float mDefaultPeakRefreshRate;
+
+        SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
+            super(handler);
+            mContext = context;
+            mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
+                    R.integer.config_defaultPeakRefreshRate);
+        }
+
+        public void observe() {
+            final ContentResolver cr = mContext.getContentResolver();
+            cr.registerContentObserver(mRefreshRateSetting, false /*notifyDescendants*/, this,
+                    UserHandle.USER_SYSTEM);
+            cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
+                    UserHandle.USER_SYSTEM);
+            synchronized (mLock) {
+                updateRefreshRateSettingLocked();
+                updateLowPowerModeSettingLocked();
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            synchronized (mLock) {
+                if (mRefreshRateSetting.equals(uri)) {
+                    updateRefreshRateSettingLocked();
+                } else if (mLowPowerModeSetting.equals(uri)) {
+                    updateLowPowerModeSettingLocked();
+                }
+            }
+        }
+
+        private void updateLowPowerModeSettingLocked() {
+            boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
+            final Vote vote;
+            if (inLowPowerMode) {
+                vote = Vote.forRefreshRates(0f, 60f);
+            } else {
+                vote = null;
+            }
+            updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
+        }
+
+        private void updateRefreshRateSettingLocked() {
+            float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
+                    Settings.System.PEAK_REFRESH_RATE, DEFAULT_REFRESH_RATE);
+            Vote vote = Vote.forRefreshRates(0f, peakRefreshRate);
+            updateVoteLocked(Vote.PRIORITY_USER_SETTING, vote);
+        }
+
+        public void dumpLocked(PrintWriter pw) {
+            pw.println("  SettingsObserver");
+            pw.println("    mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
+        }
+    }
+
+    final class AppRequestObserver {
+        private SparseArray<Display.Mode> mAppRequestedModeByDisplay;
+
+        AppRequestObserver() {
+            mAppRequestedModeByDisplay = new SparseArray<>();
+        }
+
+        public void setAppRequestedMode(int displayId, int modeId) {
+            synchronized (mLock) {
+                setAppRequestedModeLocked(displayId, modeId);
+            }
+        }
+
+        private void setAppRequestedModeLocked(int displayId, int modeId) {
+            final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
+            if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
+                return;
+            }
+
+            final Vote refreshRateVote;
+            final Vote sizeVote;
+            if (requestedMode != null) {
+                mAppRequestedModeByDisplay.put(displayId, requestedMode);
+                float refreshRate = requestedMode.getRefreshRate();
+                refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate);
+                sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
+                        requestedMode.getPhysicalHeight());
+            } else {
+                mAppRequestedModeByDisplay.remove(displayId);
+                refreshRateVote = null;
+                sizeVote = null;
+            }
+            updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote);
+            updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+            return;
+        }
+
+        private Display.Mode findModeByIdLocked(int displayId, int modeId) {
+            Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+            if (modes == null) {
+                return null;
+            }
+            for (Display.Mode mode : modes) {
+                if (mode.getModeId() == modeId) {
+                    return mode;
+                }
+            }
+            return null;
+        }
+
+        public void dumpLocked(PrintWriter pw) {
+            pw.println("  AppRequestObserver");
+            pw.println("    mAppRequestedModeByDisplay:");
+            for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
+                final int id = mAppRequestedModeByDisplay.keyAt(i);
+                final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
+                pw.println("    " + id + " -> " + mode);
+            }
+        }
+    }
+
+    private final class DisplayObserver implements DisplayManager.DisplayListener {
+        // Note that we can never call into DisplayManager or any of the non-POD classes it
+        // returns, while holding mLock since it may call into DMS, which might be simultaneously
+        // calling into us already holding its own lock.
+        private final Context mContext;
+        private final Handler mHandler;
+
+        DisplayObserver(Context context, Handler handler) {
+            mContext = context;
+            mHandler = handler;
+        }
+
+        public void observe() {
+            DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+            dm.registerDisplayListener(this, mHandler);
+
+            // Populate existing displays
+            SparseArray<Display.Mode[]> modes = new SparseArray<>();
+            SparseArray<Display.Mode> defaultModes = new SparseArray<>();
+            DisplayInfo info = new DisplayInfo();
+            Display[] displays = dm.getDisplays();
+            for (Display d : displays) {
+                final int displayId = d.getDisplayId();
+                d.getDisplayInfo(info);
+                modes.put(displayId, info.supportedModes);
+                defaultModes.put(displayId, info.getDefaultMode());
+            }
+            synchronized (mLock) {
+                final int size = modes.size();
+                for (int i = 0; i < size; i++) {
+                    mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
+                    mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            updateDisplayModes(displayId);
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            synchronized (mLock) {
+                mSupportedModesByDisplay.remove(displayId);
+                mDefaultModeByDisplay.remove(displayId);
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            updateDisplayModes(displayId);
+        }
+
+        private void updateDisplayModes(int displayId) {
+            Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
+            DisplayInfo info = new DisplayInfo();
+            d.getDisplayInfo(info);
+            boolean changed = false;
+            synchronized (mLock) {
+                if (!Arrays.equals(mSupportedModesByDisplay.get(displayId), info.supportedModes)) {
+                    mSupportedModesByDisplay.put(displayId, info.supportedModes);
+                    changed = true;
+                }
+                if (!Objects.equals(mDefaultModeByDisplay.get(displayId), info.getDefaultMode())) {
+                    changed = true;
+                    mDefaultModeByDisplay.put(displayId, info.getDefaultMode());
+                }
+                if (changed) {
+                    notifyAllowedModesChangedLocked();
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 77df10b..5e5ef26 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -112,16 +112,18 @@
                 activeColorMode = Display.COLOR_MODE_INVALID;
             }
             int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
+            int[] allowedConfigs = SurfaceControl.getAllowedDisplayConfigs(displayToken);
             LocalDisplayDevice device = mDevices.get(physicalDisplayId);
             if (device == null) {
                 // Display was added.
                 final boolean isInternal = mDevices.size() == 0;
                 device = new LocalDisplayDevice(displayToken, physicalDisplayId,
-                        configs, activeConfig, colorModes, activeColorMode, isInternal);
+                        configs, activeConfig, allowedConfigs, colorModes, activeColorMode,
+                        isInternal);
                 mDevices.put(physicalDisplayId, device);
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
             } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
-                        colorModes, activeColorMode)) {
+                        allowedConfigs, colorModes, activeColorMode)) {
                 // Display properties changed.
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
             }
@@ -167,26 +169,30 @@
         private boolean mHavePendingChanges;
         private int mState = Display.STATE_UNKNOWN;
         private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT;
-        private int mActivePhysIndex;
         private int mDefaultModeId;
         private int mActiveModeId;
         private boolean mActiveModeInvalid;
+        private int[] mAllowedModeIds;
+        private boolean mAllowedModeIdsInvalid;
+        private int mActivePhysIndex;
+        private int[] mAllowedPhysIndexes;
         private int mActiveColorMode;
         private boolean mActiveColorModeInvalid;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mSidekickActive;
         private SidekickInternal mSidekickInternal;
 
-        private  SurfaceControl.PhysicalDisplayInfo mDisplayInfos[];
+        private SurfaceControl.PhysicalDisplayInfo[] mDisplayInfos;
 
         LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
-                int[] colorModes, int activeColorMode, boolean isInternal) {
+                int[] allowedDisplayInfos, int[] colorModes, int activeColorMode,
+                boolean isInternal) {
             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
             mPhysicalDisplayId = physicalDisplayId;
             mIsInternal = isInternal;
             updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo,
-                    colorModes, activeColorMode);
+                    allowedDisplayInfos, colorModes, activeColorMode);
             updateColorModesLocked(colorModes, activeColorMode);
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
             if (mIsInternal) {
@@ -205,9 +211,10 @@
 
         public boolean updatePhysicalDisplayInfoLocked(
                 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
-                int[] colorModes, int activeColorMode) {
+                int[] allowedDisplayInfos, int[] colorModes, int activeColorMode) {
             mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
             mActivePhysIndex = activeDisplayInfo;
+            mAllowedPhysIndexes = Arrays.copyOf(allowedDisplayInfos, allowedDisplayInfos.length);
             // Build an updated list of all existing modes.
             ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
             boolean modesAdded = false;
@@ -246,8 +253,9 @@
                     break;
                 }
             }
-            // Check whether surface flinger spontaneously changed modes out from under us. Schedule
-            // traversals to ensure that the correct state is reapplied if necessary.
+
+            // Check whether surface flinger spontaneously changed modes out from under us.
+            // Schedule traversals to ensure that the correct state is reapplied if necessary.
             if (mActiveModeId != 0
                     && mActiveModeId != activeRecord.mMode.getModeId()) {
                 mActiveModeInvalid = true;
@@ -266,6 +274,7 @@
             for (DisplayModeRecord record : records) {
                 mSupportedModes.put(record.mMode.getModeId(), record);
             }
+
             // Update the default mode, if needed.
             if (findDisplayInfoIndexLocked(mDefaultModeId) < 0) {
                 if (mDefaultModeId != 0) {
@@ -274,6 +283,7 @@
                 }
                 mDefaultModeId = activeRecord.mMode.getModeId();
             }
+
             // Determine whether the active mode is still there.
             if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
                 if (mActiveModeId != 0) {
@@ -284,6 +294,21 @@
                 mActiveModeInvalid = true;
             }
 
+            // Determine what the currently allowed modes are
+            mAllowedModeIds = new int[] { mActiveModeId };
+            int[] allowedModeIds = new int[mAllowedPhysIndexes.length];
+            int size = 0;
+            for (int physIndex : mAllowedPhysIndexes) {
+                int modeId = findMatchingModeIdLocked(physIndex);
+                if (modeId > 0) {
+                    allowedModeIds[size++] = modeId;
+                }
+            }
+
+            // If this is different from our desired allowed modes, then mark our current set as
+            // invalid so we correct this on the next traversal.
+            mAllowedModeIdsInvalid = !Arrays.equals(allowedModeIds, mAllowedModeIds);
+
             // Schedule traversals so that we apply pending changes.
             sendTraversalRequestLocked();
             return true;
@@ -368,11 +393,7 @@
                 mInfo.height = phys.height;
                 mInfo.modeId = mActiveModeId;
                 mInfo.defaultModeId = mDefaultModeId;
-                mInfo.supportedModes = new Display.Mode[mSupportedModes.size()];
-                for (int i = 0; i < mSupportedModes.size(); i++) {
-                    DisplayModeRecord record = mSupportedModes.valueAt(i);
-                    mInfo.supportedModes[i] = record.mMode;
-                }
+                mInfo.supportedModes = getDisplayModes(mSupportedModes);
                 mInfo.colorMode = mActiveColorMode;
                 mInfo.supportedColorModes =
                         new int[mSupportedColorModes.size()];
@@ -593,44 +614,104 @@
         }
 
         @Override
-        public void requestDisplayModesLocked(int colorMode, int modeId) {
-            if (requestModeLocked(modeId) ||
-                    requestColorModeLocked(colorMode)) {
+        public void setRequestedColorModeLocked(int colorMode) {
+            if (requestColorModeLocked(colorMode)) {
                 updateDeviceInfoLocked();
             }
         }
 
         @Override
+        public void setAllowedDisplayModesLocked(int[] modes) {
+            updateAllowedModesLocked(modes);
+        }
+
+        @Override
         public void onOverlayChangedLocked() {
             updateDeviceInfoLocked();
         }
 
-        public boolean requestModeLocked(int modeId) {
-            if (modeId == 0) {
-                modeId = mDefaultModeId;
-            } else if (mSupportedModes.indexOfKey(modeId) < 0) {
-                Slog.w(TAG, "Requested mode " + modeId + " is not supported by this display,"
-                        + " reverting to default display mode.");
-                modeId = mDefaultModeId;
+        public void onActivePhysicalDisplayModeChangedLocked(int physIndex) {
+            if (updateActiveModeLocked(physIndex)) {
+                updateDeviceInfoLocked();
             }
+        }
 
-            int physIndex = findDisplayInfoIndexLocked(modeId);
-            if (physIndex < 0) {
-                Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
-                        + " trying with default mode ID");
-                modeId = mDefaultModeId;
-                physIndex = findDisplayInfoIndexLocked(modeId);
-            }
-            if (mActivePhysIndex == physIndex) {
+        public boolean updateActiveModeLocked(int activePhysIndex) {
+            if (mActivePhysIndex == activePhysIndex) {
                 return false;
             }
-            SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
-            mActivePhysIndex = physIndex;
-            mActiveModeId = modeId;
-            mActiveModeInvalid = false;
+            mActivePhysIndex = activePhysIndex;
+            mActiveModeId = findMatchingModeIdLocked(activePhysIndex);
+            mActiveModeInvalid = mActiveModeId == 0;
+            if (mActiveModeInvalid) {
+                Slog.w(TAG, "In unknown mode after setting allowed configs"
+                        + ": allowedPhysIndexes=" + mAllowedPhysIndexes
+                        + ", activePhysIndex=" + mActivePhysIndex);
+            }
             return true;
         }
 
+        public void updateAllowedModesLocked(int[] allowedModes) {
+            if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) {
+                return;
+            }
+            if (updateAllowedModesInternalLocked(allowedModes)) {
+                updateDeviceInfoLocked();
+            }
+        }
+
+        public boolean updateAllowedModesInternalLocked(int[] allowedModes) {
+            if (DEBUG) {
+                Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes="
+                        + Arrays.toString(allowedModes) + ")");
+            }
+            int[] allowedPhysIndexes = new int[allowedModes.length];
+            int size = 0;
+            for (int modeId : allowedModes) {
+                int physIndex = findDisplayInfoIndexLocked(modeId);
+                if (physIndex < 0) {
+                    Slog.w(TAG, "Requested mode ID " + modeId + " not available,"
+                            + " dropping from allowed set.");
+                } else {
+                    allowedPhysIndexes[size++] = physIndex;
+                }
+            }
+
+            // If we couldn't find one or more of the suggested allowed modes then we need to
+            // shrink the array to its actual size.
+            if (size != allowedModes.length) {
+                allowedPhysIndexes = Arrays.copyOf(allowedPhysIndexes, size);
+            }
+
+            // If we found no suitable modes, then we try again with the default mode which we
+            // assume has a suitable physical config.
+            if (size == 0) {
+                if (DEBUG) {
+                    Slog.w(TAG, "No valid modes allowed, falling back to default mode (id="
+                            + mDefaultModeId + ")");
+                }
+                allowedModes = new int[] { mDefaultModeId };
+                allowedPhysIndexes = new int[] { findDisplayInfoIndexLocked(mDefaultModeId) };
+            }
+
+            mAllowedModeIds = allowedModes;
+            mAllowedModeIdsInvalid = false;
+
+            if (Arrays.equals(mAllowedPhysIndexes, allowedPhysIndexes)) {
+                return false;
+            }
+            mAllowedPhysIndexes = allowedPhysIndexes;
+
+            if (DEBUG) {
+                Slog.w(TAG, "Setting allowed physical configs: allowedPhysIndexes="
+                        + Arrays.toString(allowedPhysIndexes));
+            }
+
+            SurfaceControl.setAllowedDisplayConfigs(getDisplayTokenLocked(), allowedPhysIndexes);
+            int activePhysIndex = SurfaceControl.getActiveConfig(getDisplayTokenLocked());
+            return updateActiveModeLocked(activePhysIndex);
+        }
+
         public boolean requestColorModeLocked(int colorMode) {
             if (mActiveColorMode == colorMode) {
                 return false;
@@ -650,9 +731,13 @@
         public void dumpLocked(PrintWriter pw) {
             super.dumpLocked(pw);
             pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
+            pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes));
+            pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds));
+            pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid);
             pw.println("mActivePhysIndex=" + mActivePhysIndex);
             pw.println("mActiveModeId=" + mActiveModeId);
             pw.println("mActiveColorMode=" + mActiveColorMode);
+            pw.println("mDefaultModeId=" + mDefaultModeId);
             pw.println("mState=" + Display.stateToString(mState));
             pw.println("mBrightness=" + mBrightness);
             pw.println("mBacklight=" + mBacklight);
@@ -687,10 +772,31 @@
             return -1;
         }
 
+        private int findMatchingModeIdLocked(int physIndex) {
+            SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[physIndex];
+            for (int i = 0; i < mSupportedModes.size(); i++) {
+                DisplayModeRecord record = mSupportedModes.valueAt(i);
+                if (record.hasMatchingMode(info)) {
+                    return record.mMode.getModeId();
+                }
+            }
+            return 0;
+        }
+
         private void updateDeviceInfoLocked() {
             mInfo = null;
             sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
         }
+
+        private Display.Mode[] getDisplayModes(SparseArray<DisplayModeRecord> records) {
+            final int size = records.size();
+            Display.Mode[] modes = new Display.Mode[size];
+            for (int i = 0; i < size; i++) {
+                DisplayModeRecord record = records.valueAt(i);
+                modes[i] = record.mMode;
+            }
+            return modes;
+        }
     }
 
     /** Supplies a context whose Resources apply runtime-overlays */
@@ -745,12 +851,23 @@
         }
 
         @Override
-        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int physIndex) {
             if (DEBUG) {
                 Slog.d(TAG, "onConfigChanged("
                         + "timestampNanos=" + timestampNanos
-                        + ", builtInDisplayId=" + physicalDisplayId
-                        + ", configId=" + configId + ")");
+                        + ", physicalDisplayId=" + physicalDisplayId
+                        + ", physIndex=" + physIndex + ")");
+            }
+            synchronized (getSyncRoot()) {
+                LocalDisplayDevice device = mDevices.get(physicalDisplayId);
+                if (device == null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Received config change for unhandled physical display: "
+                                + "physicalDisplayId=" + physicalDisplayId);
+                    }
+                    return;
+                }
+                device.onActivePhysicalDisplayModeChangedLocked(physIndex);
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b21f0a7..a7b9051 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -87,7 +87,7 @@
     // True if the logical display has unique content.
     private boolean mHasContent;
 
-    private int mRequestedModeId;
+    private int[] mAllowedDisplayModes = new int[0];
     private int mRequestedColorMode;
 
     // The display offsets to apply to the display projection.
@@ -354,12 +354,14 @@
         // Set the layer stack.
         device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
 
-        // Set the color mode and mode.
+        // Set the color mode and allowed display mode.
         if (device == mPrimaryDisplayDevice) {
-            device.requestDisplayModesLocked(
-                    mRequestedColorMode, mRequestedModeId);
+            device.setAllowedDisplayModesLocked(mAllowedDisplayModes);
+            device.setRequestedColorModeLocked(mRequestedColorMode);
         } else {
-            device.requestDisplayModesLocked(0, 0);  // Revert to default.
+            // Reset to default for non primary displays
+            device.setAllowedDisplayModesLocked(new int[] {0});
+            device.setRequestedColorModeLocked(0);
         }
 
         // Only grab the display info now as it may have been changed based on the requests above.
@@ -463,17 +465,17 @@
     }
 
     /**
-     * Requests the given mode.
+     * Sets the display modes the system is free to switch between.
      */
-    public void setRequestedModeIdLocked(int modeId) {
-        mRequestedModeId = modeId;
+    public void setAllowedDisplayModesLocked(int[] modes) {
+        mAllowedDisplayModes = modes;
     }
 
     /**
-     * Returns the pending requested mode.
+     * Returns the display modes the system is free to switch between.
      */
-    public int getRequestedModeIdLocked() {
-        return mRequestedModeId;
+    public int[] getAllowedDisplayModesLocked() {
+        return mAllowedDisplayModes;
     }
 
     /**
@@ -532,7 +534,7 @@
         pw.println("mDisplayId=" + mDisplayId);
         pw.println("mLayerStack=" + mLayerStack);
         pw.println("mHasContent=" + mHasContent);
-        pw.println("mRequestedMode=" + mRequestedModeId);
+        pw.println("mAllowedDisplayModes=" + Arrays.toString(mAllowedDisplayModes));
         pw.println("mRequestedColorMode=" + mRequestedColorMode);
         pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
         pw.println("mDisplayScalingDisabled=" + mDisplayScalingDisabled);
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 2f507d1..60cfbd0 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -16,9 +16,6 @@
 
 package com.android.server.display;
 
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-
 import android.content.Context;
 import android.database.ContentObserver;
 import android.graphics.SurfaceTexture;
@@ -32,6 +29,9 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -315,7 +315,16 @@
         }
 
         @Override
-        public void requestDisplayModesLocked(int color, int id) {
+        public void setAllowedDisplayModesLocked(int[] modes) {
+            final int id;
+            if (modes.length > 0) {
+                // The allowed modes should be ordered by preference, so just use the first mode
+                // here.
+                id = modes[0];
+            } else {
+                // If we don't have any allowed modes, just use the default mode.
+                id = 0;
+            }
             int index = -1;
             if (id == 0) {
                 // Use the default.
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9ab9975..71ec5b0 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -142,7 +142,6 @@
 
     // these need to match ElapsedRealtimeFlags enum in types.hal
     private static final int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1;
-    private static final int ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS = 2;
 
     // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
     private static final int GPS_DELETE_EPHEMERIS = 0x0001;
@@ -769,18 +768,15 @@
         float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
         long timestamp = location.getTime();
 
-        int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS
-                | (location.hasElapsedRealtimeUncertaintyNanos()
-                        ? ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS : 0);
+        int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS;
         long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
-        long elapsedRealtimeUncertaintyNanos = location.getElapsedRealtimeUncertaintyNanos();
 
         native_inject_best_location(
                 gnssLocationFlags, latitudeDegrees, longitudeDegrees,
                 altitudeMeters, speedMetersPerSec, bearingDegrees,
                 horizontalAccuracyMeters, verticalAccuracyMeters,
                 speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
-                elapsedRealtimeFlags, elapsedRealtimeNanos, elapsedRealtimeUncertaintyNanos);
+                elapsedRealtimeFlags, elapsedRealtimeNanos);
     }
 
     /** Returns true if the location request is too frequent. */
@@ -2174,8 +2170,7 @@
             double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
             float horizontalAccuracyMeters, float verticalAccuracyMeters,
             float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
-            long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos,
-            long elapsedRealtimeUncertaintyNanos);
+            long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos);
 
     private native void native_inject_location(double latitude, double longitude, float accuracy);
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 47b9c27..1b14ce2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -53,6 +53,7 @@
 import com.android.server.locksettings.recoverablekeystore.certificate.CertXml;
 import com.android.server.locksettings.recoverablekeystore.certificate.SigXml;
 import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
+import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -100,6 +101,7 @@
     private final PlatformKeyManager mPlatformKeyManager;
     private final ApplicationKeyStorage mApplicationKeyStorage;
     private final TestOnlyInsecureCertificateHelper mTestCertHelper;
+    private final CleanupManager mCleanupManager;
 
     /**
      * Returns a new or existing instance.
@@ -122,16 +124,24 @@
                 throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
             }
 
+            RecoverySnapshotStorage snapshotStorage =
+                    RecoverySnapshotStorage.newInstance();
+            CleanupManager cleanupManager = CleanupManager.getInstance(
+                    context.getApplicationContext(),
+                    snapshotStorage,
+                    db,
+                    applicationKeyStorage);
             mInstance = new RecoverableKeyStoreManager(
                     context.getApplicationContext(),
                     db,
                     new RecoverySessionStorage(),
                     Executors.newSingleThreadExecutor(),
-                    RecoverySnapshotStorage.newInstance(),
+                    snapshotStorage,
                     new RecoverySnapshotListenersStorage(),
                     platformKeyManager,
                     applicationKeyStorage,
-                    new TestOnlyInsecureCertificateHelper());
+                    new TestOnlyInsecureCertificateHelper(),
+                    cleanupManager);
         }
         return mInstance;
     }
@@ -146,7 +156,8 @@
             RecoverySnapshotListenersStorage listenersStorage,
             PlatformKeyManager platformKeyManager,
             ApplicationKeyStorage applicationKeyStorage,
-            TestOnlyInsecureCertificateHelper TestOnlyInsecureCertificateHelper) {
+            TestOnlyInsecureCertificateHelper testOnlyInsecureCertificateHelper,
+            CleanupManager cleanupManager) {
         mContext = context;
         mDatabase = recoverableKeyStoreDb;
         mRecoverySessionStorage = recoverySessionStorage;
@@ -155,8 +166,10 @@
         mSnapshotStorage = snapshotStorage;
         mPlatformKeyManager = platformKeyManager;
         mApplicationKeyStorage = applicationKeyStorage;
-        mTestCertHelper = TestOnlyInsecureCertificateHelper;
-
+        mTestCertHelper = testOnlyInsecureCertificateHelper;
+        mCleanupManager = cleanupManager;
+        // Clears data for removed users.
+        mCleanupManager.verifyKnownUsers();
         try {
             mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
         } catch (NoSuchAlgorithmException e) {
@@ -955,6 +968,9 @@
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.RECOVER_KEYSTORE,
                 "Caller " + Binder.getCallingUid() + " doesn't have RecoverKeyStore permission.");
+        int userId = UserHandle.getCallingUserId();
+        int uid = Binder.getCallingUid();
+        mCleanupManager.registerRecoveryAgent(userId, uid);
     }
 
     private boolean publicKeysMatch(PublicKey publicKey, byte[] vaultParams) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
new file mode 100644
index 0000000..be35b50
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
@@ -0,0 +1,178 @@
+/*
+ * 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.locksettings.recoverablekeystore.storage;
+
+import android.content.Context;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.WrappedKey;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Cleans up data when user is removed.
+ */
+public class CleanupManager {
+    private static final String TAG = "CleanupManager";
+
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private final RecoverableKeyStoreDb mDatabase;
+    private final RecoverySnapshotStorage mSnapshotStorage;
+    private final ApplicationKeyStorage mApplicationKeyStorage;
+
+    // Serial number can not be changed at runtime.
+    private Map<Integer, Long> mSerialNumbers; // Always in sync with the database.
+
+    /**
+     * Creates a new instance of the class.
+     * IMPORTANT: {@code verifyKnownUsers} must be called before the first data access.
+     */
+    public static CleanupManager getInstance(
+            Context context,
+            RecoverySnapshotStorage snapshotStorage,
+            RecoverableKeyStoreDb recoverableKeyStoreDb,
+            ApplicationKeyStorage applicationKeyStorage) {
+        return new CleanupManager(
+                context,
+                snapshotStorage,
+                recoverableKeyStoreDb,
+                UserManager.get(context),
+                applicationKeyStorage);
+    }
+
+    @VisibleForTesting
+    CleanupManager(
+            Context context,
+            RecoverySnapshotStorage snapshotStorage,
+            RecoverableKeyStoreDb recoverableKeyStoreDb,
+            UserManager userManager,
+            ApplicationKeyStorage applicationKeyStorage) {
+        mContext = context;
+        mSnapshotStorage = snapshotStorage;
+        mDatabase = recoverableKeyStoreDb;
+        mUserManager = userManager;
+        mApplicationKeyStorage = applicationKeyStorage;
+    }
+
+    /**
+     * Registers recovery agent in the system, if necessary.
+     */
+    public synchronized void registerRecoveryAgent(int userId, int uid) {
+        if (mSerialNumbers == null) {
+            // Table was uninitialized.
+            verifyKnownUsers();
+        }
+        // uid is ignored since recovery agent is a system app.
+        Long storedSerialNumber =  mSerialNumbers.get(userId);
+        if (storedSerialNumber == null) {
+            storedSerialNumber = -1L;
+        }
+        if (storedSerialNumber != -1) {
+            // User was already registered.
+            return;
+        }
+        // User was added after {@code verifyAllUsers} call.
+        long currentSerialNumber = mUserManager.getSerialNumberForUser(UserHandle.of(userId));
+        if (currentSerialNumber != -1) {
+            storeUserSerialNumber(userId, currentSerialNumber);
+        }
+    }
+
+    /**
+     * Removes data if serial number for a user was changed.
+     */
+    public synchronized void verifyKnownUsers() {
+        mSerialNumbers =  mDatabase.getUserSerialNumbers();
+        List<Integer> deletedUserIds = new ArrayList<Integer>(){};
+        for (Map.Entry<Integer, Long> entry : mSerialNumbers.entrySet()) {
+            Integer userId = entry.getKey();
+            Long storedSerialNumber = entry.getValue();
+            if (storedSerialNumber == null) {
+                storedSerialNumber = -1L;
+            }
+            long currentSerialNumber = mUserManager.getSerialNumberForUser(UserHandle.of(userId));
+            if (currentSerialNumber == -1) {
+                // User was removed.
+                deletedUserIds.add(userId);
+                removeDataForUser(userId);
+            } else if (storedSerialNumber == -1) {
+                // User is detected for the first time
+                storeUserSerialNumber(userId, currentSerialNumber);
+            } else if (storedSerialNumber != currentSerialNumber) {
+                // User has unexpected serial number - delete data related to old serial number.
+                deletedUserIds.add(userId);
+                removeDataForUser(userId);
+                // Register new user.
+                storeUserSerialNumber(userId, currentSerialNumber);
+            }
+        }
+
+        for (Integer deletedUser : deletedUserIds) {
+            mSerialNumbers.remove(deletedUser);
+        }
+    }
+
+    private void storeUserSerialNumber(int userId, long userSerialNumber) {
+        Log.d(TAG, "Storing serial number for user " + userId + ".");
+        mSerialNumbers.put(userId, userSerialNumber);
+        mDatabase.setUserSerialNumber(userId, userSerialNumber);
+    }
+
+    /**
+     * Removes all data for given user, including
+     *
+     * <ul>
+     *     <li> Recovery snapshots for all agents belonging to the {@code userId}.
+     *     <li> Entries with data related to {@code userId} from the database.
+     * </ul>
+     */
+    private void removeDataForUser(int userId) {
+        Log.d(TAG, "Removing data for user " + userId + ".");
+        List<Integer> recoveryAgents = mDatabase.getRecoveryAgents(userId);
+        for (Integer uid : recoveryAgents) {
+            mSnapshotStorage.remove(uid);
+            removeAllKeysForRecoveryAgent(userId, uid);
+        }
+
+        mDatabase.removeUserFromAllTables(userId);
+    }
+
+    /**
+     * Removes keys from Android KeyStore for the recovery agent;
+     * Doesn't remove encrypted key material from the database.
+     */
+    private void removeAllKeysForRecoveryAgent(int userId, int uid) {
+        int generationId = mDatabase.getPlatformKeyGenerationId(userId);
+        Map<String, WrappedKey> allKeys = mDatabase.getAllKeys(userId, uid, generationId);
+        for (String alias : allKeys.keySet()) {
+            try {
+                // Delete KeyStore copy.
+                mApplicationKeyStorage.deleteEntry(userId, uid, alias);
+            } catch (ServiceSpecificException e) {
+                // Ignore errors during key removal.
+                Log.e(TAG, "Error while removing recoverable key " + alias + " : " + e);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index dffaffe..3f5ac8e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -24,6 +24,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.security.keystore.recovery.RecoveryController;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.server.locksettings.recoverablekeystore.TestOnlyInsecureCertificateHelper;
@@ -261,7 +262,7 @@
      *
      * @hide
      */
-    public Map<String, WrappedKey> getAllKeys(int userId, int recoveryAgentUid,
+    public @NonNull Map<String, WrappedKey> getAllKeys(int userId, int recoveryAgentUid,
             int platformKeyGenerationId) {
         SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
         String[] projection = {
@@ -337,6 +338,58 @@
     }
 
     /**
+     * Returns serial numbers associated with all known users.
+     * -1 is used for uninitialized serial numbers.
+     *
+     * See {@code UserHandle.getSerialNumberForUser}.
+     * @return Map from userId to serial numbers.
+     */
+    public @NonNull Map<Integer, Long> getUserSerialNumbers() {
+        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+        String[] projection = {
+                UserMetadataEntry.COLUMN_NAME_USER_ID,
+                UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER};
+        String selection = null; // get all rows.
+        String[] selectionArguments = {};
+
+        try (
+            Cursor cursor = db.query(
+                UserMetadataEntry.TABLE_NAME,
+                projection,
+                selection,
+                selectionArguments,
+                /*groupBy=*/ null,
+                /*having=*/ null,
+                /*orderBy=*/ null)
+        ) {
+            Map<Integer, Long> serialNumbers = new ArrayMap<>();
+            while (cursor.moveToNext()) {
+                int userId = cursor.getInt(
+                        cursor.getColumnIndexOrThrow(UserMetadataEntry.COLUMN_NAME_USER_ID));
+                long serialNumber = cursor.getLong(cursor.getColumnIndexOrThrow(
+                        UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER));
+                serialNumbers.put(userId, serialNumber);
+            }
+            return serialNumbers;
+        }
+    }
+
+    /**
+     * Sets the {@code serialNumber} for the user {@code userId}.
+     *
+     * @return The primary key of the inserted row, or -1 if failed.
+     */
+    public long setUserSerialNumber(int userId, long serialNumber) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
+        values.put(UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER, serialNumber);
+        long result = db.replace(
+                UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
+        return result;
+    }
+
+    /**
      * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
      */
     public void invalidateKeysWithOldGenerationId(int userId, int newGenerationId) {
@@ -424,8 +477,7 @@
      */
     @Nullable
     public Long getRecoveryServiceCertSerial(int userId, int uid, @NonNull String rootAlias) {
-        return getLong(userId, uid, rootAlias,
-                RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
+        return getLong(userId, uid, rootAlias, RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL);
     }
 
     /**
@@ -441,7 +493,7 @@
      */
     public long setRecoveryServiceCertSerial(int userId, int uid, @NonNull String rootAlias,
             long serial) {
-        return setLong(userId, uid, rootAlias, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL,
+        return setLong(userId, uid, rootAlias, RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL,
                 serial);
     }
 
@@ -457,8 +509,7 @@
      */
     @Nullable
     public CertPath getRecoveryServiceCertPath(int userId, int uid, @NonNull String rootAlias) {
-        byte[] bytes = getBytes(userId, uid, rootAlias,
-                RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
+        byte[] bytes = getBytes(userId, uid, rootAlias, RootOfTrustEntry.COLUMN_NAME_CERT_PATH);
         if (bytes == null) {
             return null;
         }
@@ -489,7 +540,7 @@
         if (certPath.getCertificates().size() == 0) {
             throw new CertificateEncodingException("No certificate contained in the cert path.");
         }
-        return setBytes(userId, uid, rootAlias, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
+        return setBytes(userId, uid, rootAlias, RootOfTrustEntry.COLUMN_NAME_CERT_PATH,
                 certPath.getEncoded(CERT_PATH_ENCODING));
     }
 
@@ -1189,6 +1240,63 @@
                 RootOfTrustEntry.TABLE_NAME, values, selection, selectionArguments);
     }
 
+    /**
+     * Removes all entries for given {@code userId}.
+     */
+    public void removeUserFromAllTables(int userId) {
+        removeUserFromKeysTable(userId);
+        removeUserFromUserMetadataTable(userId);
+        removeUserFromRecoveryServiceMetadataTable(userId);
+        removeUserFromRootOfTrustTable(userId);
+    }
+
+    /**
+     * Removes all entries for given userId from Keys table.
+     *
+     * @return {@code true} if deleted a row.
+     */
+    private boolean removeUserFromKeysTable(int userId) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        String selection = KeysEntry.COLUMN_NAME_USER_ID + " = ?";
+        String[] selectionArgs = {Integer.toString(userId)};
+        return db.delete(KeysEntry.TABLE_NAME, selection, selectionArgs) > 0;
+    }
+
+    /**
+     * Removes all entries for given userId from UserMetadata table.
+     *
+     * @return {@code true} if deleted a row.
+     */
+    private boolean removeUserFromUserMetadataTable(int userId) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        String selection = UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+        String[] selectionArgs = {Integer.toString(userId)};
+        return db.delete(UserMetadataEntry.TABLE_NAME, selection, selectionArgs) > 0;
+    }
+
+    /**
+     * Removes all entries for given userId from RecoveryServiceMetadata table.
+     *
+     * @return {@code true} if deleted a row.
+     */
+    private boolean removeUserFromRecoveryServiceMetadataTable(int userId) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        String selection = RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+        String[] selectionArgs = {Integer.toString(userId)};
+        return db.delete(RecoveryServiceMetadataEntry.TABLE_NAME, selection, selectionArgs) > 0;
+    }
+
+    /**
+     * Removes all entries for given userId from RootOfTrust table.
+     *
+     * @return {@code true} if deleted a row.
+     */
+    private boolean removeUserFromRootOfTrustTable(int userId) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        String selection = RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ?";
+        String[] selectionArgs = {Integer.toString(userId)};
+        return db.delete(RootOfTrustEntry.TABLE_NAME, selection, selectionArgs) > 0;
+    }
 
     /**
      * Creates an empty row in the recovery service metadata table if such a row doesn't exist for
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index b58ee4b..e79d117 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -20,6 +20,8 @@
 
 /**
  * Contract for recoverable key database. Describes the tables present.
+ *
+ * Make sure that {@code removeUserFromAllKnownTables} is updated, when new table is added.
  */
 class RecoverableKeyStoreDbContract {
     /**
@@ -91,6 +93,11 @@
          * is used to wrap recoverable keys on disk.
          */
         static final String COLUMN_NAME_PLATFORM_KEY_GENERATION_ID = "platform_key_generation_id";
+
+        /**
+         * Serial number for the user which can not be reused. Default value is {@code -1}.
+         */
+        static final String COLUMN_NAME_USER_SERIAL_NUMBER = "user_serial_number";
     }
 
     /**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index b0613da..cd5e8cf 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -32,7 +32,7 @@
 class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
     private static final String TAG = "RecoverableKeyStoreDbHp";
 
-    static final int DATABASE_VERSION = 5;
+    static final int DATABASE_VERSION = 6; // Added user id serial number.
     private static final String DATABASE_NAME = "recoverablekeystore.db";
 
     private static final String SQL_CREATE_KEYS_ENTRY =
@@ -54,7 +54,8 @@
             "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
                     + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
                     + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
-                    + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
+                    + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER,"
+                    + UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER + " INTEGER DEFAULT -1)";
 
     private static final String SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY =
             "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
@@ -141,6 +142,11 @@
             oldVersion = 5;
         }
 
+        if (oldVersion < 6 && newVersion >= 6) {
+            upgradeDbForVersion6(db);
+            oldVersion = 6;
+        }
+
         if (oldVersion != newVersion) {
             Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
         }
@@ -179,6 +185,15 @@
                 KeysEntry.COLUMN_NAME_KEY_METADATA, "BLOB", /*defaultStr=*/ null);
     }
 
+    private void upgradeDbForVersion6(SQLiteDatabase db) {
+        Log.d(TAG, "Updating recoverable keystore database to version 6");
+        // adds a column to store the user serial number
+        addColumnToTable(db, UserMetadataEntry.TABLE_NAME,
+                UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER,
+                "INTEGER DEFAULT -1",
+                 /*defaultStr=*/ null);
+    }
+
     private static void addColumnToTable(
             SQLiteDatabase db, String tableName, String column, String columnType,
             String defaultStr) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9e912843..05af13a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -744,12 +744,8 @@
             if (!canAccessProfile(user.getIdentifier(), "Cannot access usage limit")) {
                 return null;
             }
-
-            final PackageManagerInternal pmi =
-                    LocalServices.getService(PackageManagerInternal.class);
-            final ComponentName cn = pmi.getDefaultHomeActivity(user.getIdentifier());
-            if (!cn.getPackageName().equals(callingPackage)) {
-                throw new SecurityException("Caller is not the active launcher");
+            if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
+                throw new SecurityException("Caller is not the recents app");
             }
 
             final UsageStatsManagerInternal.AppUsageLimitData data =
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index b72e836..a3b72fd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -481,6 +481,12 @@
             }
         }
 
+        if (callingUid == Process.SYSTEM_UID) {
+            params.installFlags |= PackageManager.INSTALL_RESPECT_ALLOW_DOWNGRADE;
+        } else {
+            params.installFlags &= ~PackageManager.INSTALL_RESPECT_ALLOW_DOWNGRADE;
+        }
+
         boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
         if (params.isStaged || isApex) {
             mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 3218c86..ff81ad5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -806,7 +806,7 @@
      */
     public static boolean isDowngradePermitted(int installFlags, int applicationFlags) {
         // If installed, the package will get access to data left on the device by its
-        // predecessor. As a security measure, this is permited only if this is not a
+        // predecessor. As a security measure, this is permitted only if this is not a
         // version downgrade or if the predecessor package is marked as debuggable and
         // a downgrade is explicitly requested.
         //
@@ -818,12 +818,21 @@
         // installFlags. This is because we aim to keep the behavior of debuggable
         // platform builds as close as possible to the behavior of non-debuggable
         // platform builds.
+        //
+        // In case of user builds, downgrade is permitted only for the system server initiated
+        // sessions. This is enforced by INSTALL_RESPECT_ALLOW_DOWNGRADE flag parameter.
         final boolean downgradeRequested =
                 (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
-        final boolean packageDebuggable =
-                (applicationFlags
-                        & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-        return (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
+        if (!downgradeRequested) {
+            return false;
+        }
+        final boolean isDebuggable =
+                Build.IS_DEBUGGABLE || ((applicationFlags
+                        & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+        if (isDebuggable) {
+            return true;
+        }
+        return (installFlags & PackageManager.INSTALL_RESPECT_ALLOW_DOWNGRADE) != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index ef77140..a5d291f 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -27,12 +27,8 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.service.textclassifier.IConversationActionsCallback;
-import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextClassifierCallback;
 import android.service.textclassifier.ITextClassifierService;
-import android.service.textclassifier.ITextLanguageCallback;
-import android.service.textclassifier.ITextLinksCallback;
-import android.service.textclassifier.ITextSelectionCallback;
 import android.service.textclassifier.TextClassifierService;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -132,7 +128,7 @@
     @Override
     public void onSuggestSelection(
             TextClassificationSessionId sessionId,
-            TextSelection.Request request, ITextSelectionCallback callback)
+            TextSelection.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
@@ -155,7 +151,7 @@
     @Override
     public void onClassifyText(
             TextClassificationSessionId sessionId,
-            TextClassification.Request request, ITextClassificationCallback callback)
+            TextClassification.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
@@ -178,7 +174,7 @@
     @Override
     public void onGenerateLinks(
             TextClassificationSessionId sessionId,
-            TextLinks.Request request, ITextLinksCallback callback)
+            TextLinks.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
@@ -241,7 +237,7 @@
     public void onDetectLanguage(
             TextClassificationSessionId sessionId,
             TextLanguage.Request request,
-            ITextLanguageCallback callback) throws RemoteException {
+            ITextClassifierCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
         validateInput(mContext, request.getCallingPackageName());
@@ -264,7 +260,7 @@
     public void onSuggestConversationActions(
             TextClassificationSessionId sessionId,
             ConversationActions.Request request,
-            IConversationActionsCallback callback) throws RemoteException {
+            ITextClassifierCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
         validateInput(mContext, request.getCallingPackageName());
diff --git a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
index f26121e..f8c7447 100644
--- a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
+++ b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.provider.DeviceConfig;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.RescueParty;
 
 import java.util.ArrayList;
@@ -41,20 +42,23 @@
     /**
      * Name of the special namespace in DeviceConfig table used for communicating resets.
      */
-    private static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace";
+    @VisibleForTesting
+    public static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace";
     /**
      * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link
      * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the
      * first time flags are written to the new namespace in the {@link DeviceConfig}.
      */
-    private static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces";
+    @VisibleForTesting
+    public static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces";
     /**
      * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter
      * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently
      * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given
      * namespace flags are reset.
      */
-    private static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package";
+    @VisibleForTesting
+    public static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package";
     private static final String DELIMITER = ":";
     /**
      * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG}
@@ -97,11 +101,25 @@
      * Reset all namespaces in DeviceConfig with consumed resetMode.
      */
     public static void resetDeviceConfig(int resetMode) {
-        List<String> allKnownNamespaces = getAllKnownDeviceConfigNamespacesList();
-        for (String namespace : allKnownNamespaces) {
+        resetDeviceConfig(resetMode, getAllKnownDeviceConfigNamespacesList());
+    }
+
+    /**
+     * Reset all consumed namespaces in DeviceConfig with consumed resetMode.
+     */
+    public static void resetDeviceConfig(int resetMode, List<String> namespacesList) {
+        for (String namespace : namespacesList) {
             DeviceConfig.resetToDefaults(resetMode, namespace);
         }
-        addToKnownResetNamespaces(allKnownNamespaces);
+        addToKnownResetNamespaces(namespacesList);
+    }
+
+    /**
+     * Resets known reset namespaces flag counter for tests only.
+     */
+    @VisibleForTesting
+    public static void resetKnownResetNamespacesFlagCounterForTest() {
+        sKnownResetNamespacesFlagCounter = -1;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 19ff438..bb5a221 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -86,6 +86,7 @@
 import android.content.res.Configuration;
 import android.content.res.ResourceId;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -123,6 +124,7 @@
 import android.view.animation.TranslateAnimation;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AttributeCache;
@@ -558,19 +560,19 @@
         }
         resId = updateToTranslucentAnimIfNeeded(resId, transit);
         if (ResourceId.isValid(resId)) {
-            return AnimationUtils.loadAnimation(context, resId);
+            return loadAnimationSafely(context, resId);
         }
         return null;
     }
 
-    Animation loadAnimationRes(LayoutParams lp, int resId) {
+    private Animation loadAnimationRes(LayoutParams lp, int resId) {
         Context context = mContext;
         if (ResourceId.isValid(resId)) {
             AttributeCache.Entry ent = getCachedAnimations(lp);
             if (ent != null) {
                 context = ent.context;
             }
-            return AnimationUtils.loadAnimation(context, resId);
+            return loadAnimationSafely(context, resId);
         }
         return null;
     }
@@ -579,12 +581,22 @@
         if (ResourceId.isValid(resId)) {
             AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
             if (ent != null) {
-                return AnimationUtils.loadAnimation(ent.context, resId);
+                return loadAnimationSafely(ent.context, resId);
             }
         }
         return null;
     }
 
+    @VisibleForTesting
+    Animation loadAnimationSafely(Context context, int resId) {
+        try {
+            return AnimationUtils.loadAnimation(context, resId);
+        } catch (NotFoundException e) {
+            Slog.w(TAG, "Unable to load animation resource", e);
+            return null;
+        }
+    }
+
     private int updateToTranslucentAnimIfNeeded(int anim, int transit) {
         if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && anim == R.anim.activity_open_enter) {
             return R.anim.activity_translucent_open_enter;
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e8882ec..d39f20c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -491,10 +491,6 @@
         SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
     }
 
-    if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) {
-        SET(ElapsedUncertaintyRealtimeNanos, location.elapsedRealtime.timeUncertaintyNs);
-    }
-
     return object.get();
 }
 
@@ -525,8 +521,7 @@
         jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
         jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
         jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
-        jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
-        jlong elapsedRealtimeUncertaintyNanos) {
+        jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos) {
     GnssLocation_V2_0 location;
     location.v1_0 = createGnssLocation_V1_0(
             gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
@@ -536,7 +531,6 @@
 
     location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
     location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
-    location.elapsedRealtime.timeUncertaintyNs = static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos);
 
     return location;
 }
@@ -1893,8 +1887,7 @@
         jfloat bearingAccuracyDegrees,
         jlong timestamp,
         jint elapsedRealtimeFlags,
-        jlong elapsedRealtimeNanos,
-        jlong elapsedRealtimeUncertaintyNanos) {
+        jlong elapsedRealtimeNanos) {
     if (gnssHal_V2_0 != nullptr) {
         GnssLocation_V2_0 location = createGnssLocation_V2_0(
                 gnssLocationFlags,
@@ -1909,8 +1902,7 @@
                 bearingAccuracyDegrees,
                 timestamp,
                 elapsedRealtimeFlags,
-                elapsedRealtimeNanos,
-                elapsedRealtimeUncertaintyNanos);
+                elapsedRealtimeNanos);
         auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
 
         if (!result.isOk() || !result) {
@@ -2821,7 +2813,7 @@
             android_location_GnssLocationProvider_read_nmea)},
     {"native_inject_time", "(JJI)V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_inject_time)},
-    {"native_inject_best_location", "(IDDDFFFFFFJIJJ)V", reinterpret_cast<void *>(
+    {"native_inject_best_location", "(IDDDFFFFFFJIJ)V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_inject_best_location)},
     {"native_inject_location", "(DDF)V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_inject_location)},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f496e81..36251f5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -172,7 +172,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
-import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
@@ -5338,9 +5337,14 @@
      */
     @Override
     public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
-        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+        if (!mHasFeature) {
             return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
         }
+        if (!mLockPatternUtils.hasSecureLockScreen()) {
+            // No strong auth timeout on devices not supporting the
+            // {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature
+            return 0;
+        }
         enforceFullCrossUsersPermission(userId);
         synchronized (getLockObject()) {
             if (who != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a19d5d5..eba00812 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1881,19 +1881,6 @@
         mSystemServiceManager.startService(IncidentCompanionService.class);
         traceEnd();
 
-        if (safeMode) {
-            traceBeginAndSlog("EnterSafeModeAndDisableJitCompilation");
-            mActivityManagerService.enterSafeMode();
-            // Disable the JIT for the system_server process
-            VMRuntime.getRuntime().disableJitCompilation();
-            traceEnd();
-        } else {
-            // Enable the JIT for the system_server process
-            traceBeginAndSlog("StartJitCompilation");
-            VMRuntime.getRuntime().startJitCompilation();
-            traceEnd();
-        }
-
         // MMS service broker
         traceBeginAndSlog("StartMmsService");
         mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index c8e6782..d0158e0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -685,10 +685,10 @@
         List<OpEntry> entries = new ArrayList<>();
         entries.add(new OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
-                AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
+                AppOpsManager.MODE_IGNORED));
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
-                AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
+                AppOpsManager.MODE_IGNORED));
 
         ops.add(new PackageOps(PACKAGE_1, UID_1, entries));
 
@@ -696,7 +696,7 @@
         entries = new ArrayList<>();
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
-                AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
+                AppOpsManager.MODE_IGNORED));
 
         ops.add(new PackageOps(PACKAGE_2, UID_2, entries));
 
@@ -704,7 +704,7 @@
         entries = new ArrayList<>();
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
-                AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
+                AppOpsManager.MODE_ALLOWED));
 
         ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries));
 
@@ -712,10 +712,10 @@
         entries = new ArrayList<>();
         entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
-                AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
+                AppOpsManager.MODE_IGNORED));
         entries.add(new OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
-                AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
+                AppOpsManager.MODE_IGNORED));
 
         ops.add(new PackageOps(PACKAGE_3, UID_10_3, entries));
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index b13735c..36825af 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -35,9 +35,12 @@
 import android.os.RecoverySystem;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.am.SettingsToPropertiesMapper;
+import com.android.server.utils.FlagNamespaceUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -56,6 +59,10 @@
 public class RescuePartyTest {
     private static final int PERSISTENT_APP_UID = 12;
     private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
+    private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
+    private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
+    private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
+            {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
 
     private MockitoSession mSession;
 
@@ -73,9 +80,11 @@
                 ExtendedMockito.mockitoSession().initMocks(
                         this)
                         .strictness(Strictness.LENIENT)
+                        .spyStatic(DeviceConfig.class)
                         .spyStatic(SystemProperties.class)
                         .spyStatic(Settings.Global.class)
                         .spyStatic(Settings.Secure.class)
+                        .spyStatic(SettingsToPropertiesMapper.class)
                         .spyStatic(RecoverySystem.class)
                         .spyStatic(RescueParty.class)
                         .startMocking();
@@ -121,8 +130,17 @@
                 }
         ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
 
+        // Mock DeviceConfig
+        doAnswer((Answer<Boolean>) invocationOnMock -> true)
+                .when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(),
+                        anyBoolean()));
+        doAnswer((Answer<Void>) invocationOnMock -> null)
+                .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()));
+
+
         doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
         RescueParty.resetAllThresholds();
+        FlagNamespaceUtils.resetKnownResetNamespacesFlagCounterForTest();
 
         SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL,
                 Integer.toString(RescueParty.LEVEL_NONE));
@@ -278,10 +296,32 @@
                 SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
     }
 
+    @Test
+    public void testNativeRescuePartyResets() {
+        doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());
+        doReturn(FAKE_RESET_NATIVE_NAMESPACES).when(
+                () -> SettingsToPropertiesMapper.getResetNativeCategories());
+
+        RescueParty.onSettingsProviderPublished(mMockContext);
+
+        verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
+                FAKE_NATIVE_NAMESPACE1));
+        verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
+                FAKE_NATIVE_NAMESPACE2));
+
+        ExtendedMockito.verify(
+                () -> DeviceConfig.setProperty(FlagNamespaceUtils.NAMESPACE_RESCUE_PARTY,
+                        FlagNamespaceUtils.RESET_PLATFORM_PACKAGE_FLAG + 0,
+                        FAKE_NATIVE_NAMESPACE1, /*makeDefault=*/true));
+        ExtendedMockito.verify(
+                () -> DeviceConfig.setProperty(FlagNamespaceUtils.NAMESPACE_RESCUE_PARTY,
+                        FlagNamespaceUtils.RESET_PLATFORM_PACKAGE_FLAG + 1,
+                        FAKE_NATIVE_NAMESPACE2, /*makeDefault=*/true));
+    }
+
     private void verifySettingsResets(int resetMode) {
         verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
-                resetMode,
-                UserHandle.USER_SYSTEM));
+                resetMode, UserHandle.USER_SYSTEM));
         verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
                 eq(resetMode), anyInt()));
     }
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
index f19cc5d..4c8b4ba 100755
--- a/services/tests/runtests.py
+++ b/services/tests/runtests.py
@@ -19,7 +19,7 @@
 import sys
 
 INSTRUMENTED_PACKAGE_RUNNER = ('com.android.frameworks.servicestests/'
-                               'android.support.test.runner.AndroidJUnitRunner')
+                               'androidx.test.runner.AndroidJUnitRunner')
 
 PACKAGE_WHITELIST = (
     "com.android.server",
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
index edd89f9..96f329b 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
@@ -45,21 +45,6 @@
 
     private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
 
-    public void testWatchNotedOpsRequiresPermission() {
-        // Create a mock listener
-        final OnOpNotedListener listener = mock(OnOpNotedListener.class);
-
-        // Try to start watching noted ops
-        final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
-        try {
-            appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
-                    AppOpsManager.OP_RECORD_AUDIO}, listener);
-            fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
-        } catch (SecurityException expected) {
-            /*ignored*/
-        }
-    }
-
     @Test
     public void testWatchNotedOps() {
         // Create a mock listener
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index eb0c627..66d2bab 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -107,8 +107,8 @@
                 }
                 final AppOpsService.Op _op1 = ops.get(op1);
                 final AppOpsService.Op _op2 = ops.get(op2);
-                final int mode1 = (_op1 == null) ? defaultModeOp1 : _op1.mode;
-                final int mode2 = (_op2 == null) ? defaultModeOp2 : _op2.mode;
+                final int mode1 = (_op1 == null) ? defaultModeOp1 : _op1.getMode();
+                final int mode2 = (_op2 == null) ? defaultModeOp2 : _op2.getMode();
                 assertEquals(mode1, mode2);
                 if (mode1 != defaultModeOp1) {
                     numberOfNonDefaultOps++;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index c78b96d..5bab65c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -59,6 +59,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
+import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -154,6 +155,7 @@
     @Mock private KeyguardManager mKeyguardManager;
     @Mock private PlatformKeyManager mPlatformKeyManager;
     @Mock private ApplicationKeyStorage mApplicationKeyStorage;
+    @Mock private CleanupManager mCleanupManager;
     @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
 
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -191,7 +193,8 @@
                 mMockListenersStorage,
                 mPlatformKeyManager,
                 mApplicationKeyStorage,
-                mTestOnlyInsecureCertificateHelper);
+                mTestOnlyInsecureCertificateHelper,
+                mCleanupManager);
     }
 
     @After
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
new file mode 100644
index 0000000..0b15a12
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.locksettings.recoverablekeystore.storage;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CleanupManagerTest {
+    private static final int USER_ID = 10;
+    private static final int USER_ID_2 = 20;
+    private static final int UID = 1234;
+    private static final long USER_SERIAL_NUMBER = 101L;
+    private static final long USER_SERIAL_NUMBER_2 = 202L;
+
+    private Context mContext;
+    private CleanupManager mManager;
+
+    @Mock private RecoverableKeyStoreDb mDatabase;
+    @Mock private RecoverySnapshotStorage mRecoverySnapshotStorage;
+    @Mock private UserManager mUserManager;
+    @Mock private ApplicationKeyStorage mApplicationKeyStorage;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mManager = new CleanupManager(mContext, mRecoverySnapshotStorage, mDatabase, mUserManager,
+                mApplicationKeyStorage);
+    }
+
+    @Test
+    public void registerRecoveryAgent_unknownUser_storesInDb() throws Exception {
+        when(mDatabase.getUserSerialNumbers()).thenReturn(new HashMap<>());
+        when(mUserManager.getSerialNumberForUser(eq(UserHandle.of(USER_ID))))
+                .thenReturn(USER_SERIAL_NUMBER);
+        when(mUserManager.getSerialNumberForUser(eq(UserHandle.of(USER_ID_2))))
+                .thenReturn(USER_SERIAL_NUMBER_2);
+
+        mManager.registerRecoveryAgent(USER_ID, UID);
+        mManager.registerRecoveryAgent(USER_ID_2, UID);
+
+        verify(mDatabase).setUserSerialNumber(USER_ID, USER_SERIAL_NUMBER);
+        verify(mDatabase).setUserSerialNumber(USER_ID_2, USER_SERIAL_NUMBER_2);
+
+    }
+
+    @Test
+    public void registerRecoveryAgent_registersSameUser_doesntChangeDb() throws Exception {
+        when(mDatabase.getUserSerialNumbers()).thenReturn(new HashMap<>());
+        when(mUserManager.getSerialNumberForUser(eq(UserHandle.of(USER_ID))))
+                .thenReturn(USER_SERIAL_NUMBER);
+
+        mManager.registerRecoveryAgent(USER_ID, UID);
+        mManager.registerRecoveryAgent(USER_ID, UID); // ignored.
+
+        verify(mDatabase, times(1)).setUserSerialNumber(USER_ID, USER_SERIAL_NUMBER);
+    }
+
+    @Test
+    public void verifyKnownUsers_newSerialNumber_deletesData() throws Exception {
+        Map knownSerialNumbers = new HashMap<>();
+        knownSerialNumbers.put(USER_ID, USER_SERIAL_NUMBER);
+        when(mDatabase.getUserSerialNumbers()).thenReturn(knownSerialNumbers);
+        List<Integer> recoveryAgents = new ArrayList<>();
+        recoveryAgents.add(UID);
+        when(mDatabase.getRecoveryAgents(USER_ID)).thenReturn(recoveryAgents);
+
+        when(mUserManager.getSerialNumberForUser(eq(UserHandle.of(USER_ID))))
+                .thenReturn(USER_SERIAL_NUMBER_2); // new value
+
+
+        mManager.verifyKnownUsers();
+
+        verify(mDatabase).removeUserFromAllTables(USER_ID);
+        verify(mDatabase).setUserSerialNumber(USER_ID, USER_SERIAL_NUMBER_2);
+        verify(mRecoverySnapshotStorage).remove(UID);
+    }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
index 35215c3..2658af6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -51,6 +51,7 @@
     private static final long TEST_LAST_SYNCED_AT = 1517990732000L;
     private static final int TEST_RECOVERY_STATUS = 3;
     private static final int TEST_PLATFORM_KEY_GENERATION_ID = 11;
+    private static final int TEST_USER_SERIAL_NUMBER = 15;
     private static final int TEST_SNAPSHOT_VERSION = 31;
     private static final int TEST_SHOULD_CREATE_SNAPSHOT = 1;
     private static final byte[] TEST_PUBLIC_KEY = "test-public-key".getBytes(UTF_8);
@@ -234,5 +235,14 @@
 
         assertThat(mDatabase.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
                 .isGreaterThan(-1L);
+
+        // User serial number column was added when upgrading from v5 to v6
+        values = new ContentValues();
+        values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+        values.put(UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER, TEST_USER_SERIAL_NUMBER);
+        assertThat(
+                mDatabase.replace(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+                .isGreaterThan(-1L);
     }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 7de9ffc..932a769 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -279,6 +279,55 @@
     }
 
     @Test
+    public void getUserSerialNumbers_returnsSerialNumbers() {
+        int userId = 42;
+        int userId2 = 44;
+        Long serialNumber = 24L;
+        Long serialNumber2 = 25L;
+        mRecoverableKeyStoreDb.setUserSerialNumber(userId, serialNumber);
+        mRecoverableKeyStoreDb.setUserSerialNumber(userId2, serialNumber2);
+
+        assertEquals(2, mRecoverableKeyStoreDb.getUserSerialNumbers().size());
+        assertEquals(serialNumber, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId));
+        assertEquals(serialNumber2, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId2));
+    }
+
+    @Test
+    public void getUserSerialNumbers_returnsMinusOneIfNoEntry() {
+        int userId = 42;
+        int generationId = 24;
+        Long serialNumber = -1L;
+        // Don't set serial number
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
+
+        assertEquals(1, mRecoverableKeyStoreDb.getUserSerialNumbers().size());
+        assertEquals(serialNumber, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId));
+    }
+
+    @Test
+    public void removeUserFromAllTables_removesData() throws Exception {
+        int userId = 12;
+        int generationId = 24;
+        int[] types = new int[]{1};
+        int uid = 10009;
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS, 1234L);
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(userId, uid, "root");
+        mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types);
+
+        mRecoverableKeyStoreDb.removeUserFromAllTables(userId);
+
+        // RootOfTrust
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isNull();
+        // UserMetadata
+        assertThat(mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId)).isEqualTo(-1);
+        // RecoveryServiceMetadata
+        assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEmpty();
+    }
+
+    @Test
     public void setRecoveryStatus_withSingleKey() {
         int userId = 12;
         int uid = 1009;
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 8caa39d..1f86171 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -175,9 +175,9 @@
     /** Verify app usage limit observer is added */
     @Test
     public void testAppUsageLimitObserver_AddObserver() {
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
-        addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_30_MIN);
         assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID2));
         assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
     }
@@ -203,7 +203,7 @@
     /** Verify app usage limit observer is removed */
     @Test
     public void testAppUsageLimitObserver_RemoveObserver() {
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
         mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
         assertFalse("Observer wasn't removed", hasAppUsageLimitObserver(UID, OBS_ID1));
@@ -290,9 +290,9 @@
     /** Re-adding an observer should result in only one copy */
     @Test
     public void testAppUsageLimitObserver_ObserverReAdd() {
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_10_MIN);
         assertTrue("Observer wasn't added",
                 getAppUsageLimitObserver(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
         mController.removeAppUsageLimitObserver(UID, OBS_ID1, USER_ID);
@@ -304,7 +304,7 @@
     public void testAllObservers_ExclusiveObserverIds() {
         addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
         addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_10_MIN);
         assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
         assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
         assertTrue("Observer wasn't added", hasAppUsageLimitObserver(UID, OBS_ID1));
@@ -396,7 +396,7 @@
     @Test
     public void testAppUsageLimitObserver_Accumulation() throws Exception {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         startUsage(PKG_SOC1);
         // Add 10 mins
         setTime(TIME_10_MIN);
@@ -456,7 +456,7 @@
     @Test
     public void testAppUsageLimitObserver_TimeoutOtherApp() throws Exception {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L, 4_000L);
         startUsage(PKG_SOC2);
         assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
         setTime(6_000L);
@@ -498,7 +498,7 @@
     @Test
     public void testAppUsageLimitObserver_Timeout() throws Exception {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, 4_000L, 4_000L);
         startUsage(PKG_SOC1);
         setTime(6_000L);
         assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
@@ -551,7 +551,7 @@
         setTime(TIME_10_MIN);
         startUsage(PKG_GAME1);
         setTime(TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_30_MIN);
         setTime(TIME_30_MIN + TIME_10_MIN);
         stopUsage(PKG_GAME1);
         assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
@@ -612,7 +612,7 @@
         startUsage(PKG_SOC1);
         setTime(TIME_10_MIN);
         // 10 second time limit
-        addAppUsageLimitObserver(OBS_ID1, GROUP_SOC, 10_000L);
+        addAppUsageLimitObserver(OBS_ID1, GROUP_SOC, 10_000L, 10_000L);
         setTime(TIME_10_MIN + 5_000L);
         // Shouldn't call back in 6 seconds
         assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
@@ -692,23 +692,23 @@
     public void testAppUsageLimitObserver_MaxObserverLimit() throws Exception {
         boolean receivedException = false;
         int ANOTHER_UID = UID + 1;
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID2, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID3, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID4, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID6, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID7, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID8, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID9, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID10, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID2, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID3, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID4, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID6, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID7, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID8, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID9, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID10, GROUP1, TIME_30_MIN, TIME_30_MIN);
         // Readding an observer should not cause an IllegalStateException
-        addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_30_MIN);
         // Adding an observer for a different uid shouldn't cause an IllegalStateException
         mController.addAppUsageLimitObserver(
-                ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
+                ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, TIME_30_MIN, null, USER_ID);
         try {
-            addAppUsageLimitObserver(OBS_ID11, GROUP1, TIME_30_MIN);
+            addAppUsageLimitObserver(OBS_ID11, GROUP1, TIME_30_MIN, TIME_30_MIN);
         } catch (IllegalStateException ise) {
             receivedException = true;
         }
@@ -748,9 +748,9 @@
     public void testAppUsageLimitObserver_MinimumTimeLimit() throws Exception {
         boolean receivedException = false;
         // adding an observer with a one minute time limit should not cause an exception
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT, MIN_TIME_LIMIT);
         try {
-            addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
+            addAppUsageLimitObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1, MIN_TIME_LIMIT - 1);
         } catch (IllegalArgumentException iae) {
             receivedException = true;
         }
@@ -807,7 +807,7 @@
     @Test
     public void testAppUsageLimitObserver_ConcurrentUsage() throws Exception {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         AppTimeLimitController.UsageGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
         startUsage(PKG_SOC1);
         // Add 10 mins
@@ -967,7 +967,7 @@
     /** Verify app usage limit observer added correctly reports its total usage limit */
     @Test
     public void testAppUsageLimitObserver_GetTotalUsageLimit() {
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
         assertNotNull("Observer wasn't added", group);
         assertEquals("Observer didn't correctly report total usage limit",
@@ -978,7 +978,7 @@
     @Test
     public void testAppUsageLimitObserver_GetUsageRemaining() {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
         startUsage(PKG_SOC1);
         setTime(TIME_10_MIN);
         stopUsage(PKG_SOC1);
@@ -993,8 +993,8 @@
      */
     @Test
     public void testAppUsageLimitObserver_GetAppUsageLimit() {
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN, TIME_10_MIN);
         UsageStatsManagerInternal.AppUsageLimitData group = getAppUsageLimit(PKG_SOC1);
         assertEquals("Observer with the smallest usage limit remaining wasn't returned",
                 TIME_10_MIN, group.getTotalUsageLimit());
@@ -1006,8 +1006,8 @@
     @Test
     public void testAppUsageLimitObserver_GetAppUsageLimitUsed() {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN, TIME_10_MIN);
         startUsage(PKG_GAME1);
         setTime(TIME_10_MIN * 2 + TIME_1_MIN);
         stopUsage(PKG_GAME1);
@@ -1024,8 +1024,8 @@
     @Test
     public void testAppUsageLimitObserver_GetAppUsageLimitAllUsed() {
         setTime(0L);
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN);
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_30_MIN);
+        addAppUsageLimitObserver(OBS_ID2, GROUP_SOC, TIME_10_MIN, TIME_10_MIN);
         startUsage(PKG_SOC1);
         setTime(TIME_10_MIN);
         stopUsage(PKG_SOC1);
@@ -1035,10 +1035,21 @@
                 0L, group.getUsageRemaining());
     }
 
+    /** Verify that a limit of 0 is not allowed. */
+    @Test
+    public void testAppUsageLimitObserver_ZeroTimeLimitIsNotAllowed() {
+        try {
+            addAppUsageLimitObserver(OBS_ID1, GROUP1, 0, 0);
+            fail("timeLimit of 0 should not be allowed.");
+        } catch (IllegalArgumentException expected) {
+            // Exception expected.
+        }
+    }
+
     /** Verify that a limit of 0 is allowed for the special case of re-registering an observer. */
     @Test
-    public void testAppUsageLimitObserver_ZeroTimeLimitIsAllowed() {
-        addAppUsageLimitObserver(OBS_ID1, GROUP1, 0);
+    public void testAppUsageLimitObserver_ZeroTimeRemainingIsAllowed() {
+        addAppUsageLimitObserver(OBS_ID1, GROUP1, TIME_1_MIN, 0);
         AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
         assertNotNull("Observer wasn't added", group);
         assertEquals("Usage remaining was not 0.", 0, group.getUsageRemaining());
@@ -1066,8 +1077,10 @@
                 null, null, USER_ID);
     }
 
-    private void addAppUsageLimitObserver(int observerId, String[] packages, long timeLimit) {
-        mController.addAppUsageLimitObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+    private void addAppUsageLimitObserver(int observerId, String[] packages, long timeLimit,
+            long timeRemaining) {
+        mController.addAppUsageLimitObserver(UID, observerId, packages, timeLimit, timeRemaining,
+                null, USER_ID);
     }
 
     /** Is there still an app usage observer by that id */
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index b91f3ec..6e09167 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -23,29 +23,29 @@
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 
-import androidx.test.filters.SmallTest;
-
 import org.junit.Before;
 import org.junit.Test;
 
+import androidx.test.filters.SmallTest;
+
 /**
  * Test class for {@link AppTransition}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:AppTransitionTests
+ *  atest WmTests:AppTransitionTests
  */
 @SmallTest
 @Presubmit
@@ -167,4 +167,11 @@
         assertFalse(dc1.mOpeningApps.contains(token1));
     }
 
+    @Test
+    public void testLoadAnimationSafely() {
+        DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        assertNull(dc.mAppTransition.loadAnimationSafely(
+                getInstrumentation().getTargetContext(), -1));
+    }
+
 }
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 731cbf4..f3d6387 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -511,8 +511,10 @@
 
     class AppUsageLimitGroup extends UsageGroup {
         public AppUsageLimitGroup(UserData user, ObserverAppData observerApp, int observerId,
-                String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) {
+                String[] observed, long timeLimitMs, long timeRemainingMs,
+                PendingIntent limitReachedCallback) {
             super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+            mUsageTimeMs = timeLimitMs - timeRemainingMs;
         }
 
         @Override
@@ -839,9 +841,9 @@
      * Existing app usage limit observer with the same observerId will be removed.
      */
     public void addAppUsageLimitObserver(int requestingUid, int observerId, String[] observed,
-            long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
-        // Allow the special case of the limit being 0, but with no callback.
-        if (timeLimit != 0L && timeLimit < getMinTimeLimit()) {
+            long timeLimit, long timeRemaining, PendingIntent callbackIntent,
+            @UserIdInt int userId) {
+        if (timeLimit < getMinTimeLimit()) {
             throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
         }
         synchronized (mLock) {
@@ -859,7 +861,7 @@
                         "Too many app usage observers added by uid " + requestingUid);
             }
             group = new AppUsageLimitGroup(user, observerApp, observerId, observed, timeLimit,
-                    timeLimit == 0L ? null : callbackIntent);
+                    timeRemaining, timeRemaining == 0L ? null : callbackIntent);
             observerApp.appUsageLimitGroups.append(observerId, group);
 
             if (DEBUG) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b14d722..27fdbcb 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1399,7 +1399,8 @@
 
         @Override
         public void registerAppUsageLimitObserver(int observerId, String[] packages,
-                long timeLimitMs, PendingIntent callbackIntent, String callingPackage) {
+                long timeLimitMs, long timeRemainingMs, PendingIntent callbackIntent,
+                String callingPackage) {
             if (!hasPermissions(callingPackage,
                     Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
                 throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
@@ -1409,7 +1410,11 @@
             if (packages == null || packages.length == 0) {
                 throw new IllegalArgumentException("Must specify at least one package");
             }
-            if (callbackIntent == null && timeLimitMs != 0L) {
+            if (timeRemainingMs > timeLimitMs) {
+                throw new IllegalArgumentException(
+                        "Remaining time can't be greater than total time.");
+            }
+            if (callbackIntent == null && timeRemainingMs != 0L) {
                 throw new NullPointerException("callbackIntent can't be null");
             }
             final int callingUid = Binder.getCallingUid();
@@ -1417,7 +1422,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 UsageStatsService.this.registerAppUsageLimitObserver(callingUid, observerId,
-                        packages, timeLimitMs, callbackIntent, userId);
+                        packages, timeLimitMs, timeRemainingMs, callbackIntent, userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1545,9 +1550,9 @@
     }
 
     void registerAppUsageLimitObserver(int callingUid, int observerId, String[] packages,
-            long timeLimitMs, PendingIntent callbackIntent, int userId) {
-        mAppTimeLimit.addAppUsageLimitObserver(callingUid, observerId, packages, timeLimitMs,
-                callbackIntent, userId);
+            long timeLimitMs, long timeRemainingMs, PendingIntent callbackIntent, int userId) {
+        mAppTimeLimit.addAppUsageLimitObserver(callingUid, observerId, packages,
+                timeLimitMs, timeRemainingMs, callbackIntent, userId);
     }
 
     void unregisterAppUsageLimitObserver(int callingUid, int observerId, int userId) {
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 5ac4a46..4359978 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -29,7 +29,7 @@
 
       // test android dependencies
       "platform-test-annotations",
-      "android-support-test",
+      "androidx.test.rules",
       // test framework dependencies
       "mockito-target-inline-minus-junit4",
       // "mockito-target-minus-junit4",
diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
index 99f4add..b967e72 100644
--- a/startop/iorap/tests/AndroidManifest.xml
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -22,7 +22,7 @@
 
     <!--suppress AndroidDomInspection -->
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.google.android.startop.iorap.tests" />
 
       <!--
diff --git a/startop/iorap/tests/AndroidTest.xml b/startop/iorap/tests/AndroidTest.xml
index 919154d..bcd1103 100644
--- a/startop/iorap/tests/AndroidTest.xml
+++ b/startop/iorap/tests/AndroidTest.xml
@@ -44,7 +44,7 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.google.android.startop.iorap.tests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
 </configuration>
 
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
index 16dcbe2..b1e6194 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -14,12 +14,14 @@
 
 package com.google.android.startop.iorap
 
-import android.net.Uri
 import android.os.ServiceManager
-import android.support.test.filters.MediumTest
+import androidx.test.filters.MediumTest
 import org.junit.Test
-import org.junit.Ignore
-import org.mockito.Mockito.*
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.timeout
 
 // @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
 @MediumTest
@@ -27,7 +29,7 @@
     /**
      * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
      */
-    private val iorapService : IIorap by lazy {
+    private val iorapService: IIorap by lazy {
         // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply.
         IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"))
 
@@ -39,7 +41,7 @@
 
     // A dummy binder stub implementation is required to use with mockito#spy.
     // Mockito overrides the methods at runtime and tracks how methods were invoked.
-    open class DummyTaskListener : ITaskListener.Stub()  {
+    open class DummyTaskListener : ITaskListener.Stub() {
         // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions
         // from using the mockito matchers (eq, argThat, etc).
         override fun onProgress(requestId: RequestId?, result: TaskResult?) {
@@ -49,7 +51,7 @@
         }
     }
 
-    private fun testAnyMethod(func : (RequestId) -> Unit) {
+    private fun testAnyMethod(func: (RequestId) -> Unit) {
         val taskListener = spy(DummyTaskListener())!!
 
         try {
@@ -68,14 +70,13 @@
 
             // The "stub" behavior of iorapd is that every request immediately gets a response of
             //   BEGAN,ONGOING,COMPLETED
-            inOrder.verify(taskListener, timeout(100)).
-                  onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
-            inOrder.verify(taskListener, timeout(100)).
-                  onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
-            inOrder.verify(taskListener, timeout(100)).
-                  onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
+            inOrder.verify(taskListener, timeout(100))
+                .onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
+            inOrder.verify(taskListener, timeout(100))
+                .onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
+            inOrder.verify(taskListener, timeout(100))
+                .onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
             inOrder.verifyNoMoreInteractions()
-
         } finally {
             // iorapService.setTaskListener(null)
             // FIXME: null is broken, C++ side sees a non-null object.
@@ -96,7 +97,7 @@
 
     @Test
     fun testOnAppIntentEvent() {
-        testAnyMethod { requestId : RequestId ->
+        testAnyMethod { requestId: RequestId ->
             iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged(
                     ActivityInfo("dont care", "dont care"),
                     ActivityInfo("dont care 2", "dont care 2")))
@@ -105,7 +106,7 @@
 
     @Test
     fun testOnSystemServiceEvent() {
-        testAnyMethod { requestId : RequestId ->
+        testAnyMethod { requestId: RequestId ->
             iorapService.onSystemServiceEvent(requestId,
                     SystemServiceEvent(SystemServiceEvent.TYPE_START))
         }
@@ -113,9 +114,9 @@
 
     @Test
     fun testOnSystemServiceUserEvent() {
-        testAnyMethod { requestId : RequestId ->
+        testAnyMethod { requestId: RequestId ->
             iorapService.onSystemServiceUserEvent(requestId,
-                    SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER,0))
+                    SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 0))
         }
     }
 }
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
index 4abbb3e..8fa0cde 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -17,7 +17,7 @@
 import android.net.Uri
 import android.os.Parcel
 import android.os.Parcelable
-import android.support.test.filters.SmallTest
+import androidx.test.filters.SmallTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import com.google.common.truth.Truth.assertThat
@@ -29,7 +29,7 @@
  */
 @SmallTest
 @RunWith(Parameterized::class)
-class ParcelablesTest<T : Parcelable>(private val inputData : InputData<T>) {
+class ParcelablesTest<T : Parcelable>(private val inputData: InputData<T>) {
     companion object {
         private val initialRequestId = RequestId.nextValueForSequence()!!
 
@@ -73,19 +73,19 @@
                         TaskResult(TaskResult.STATE_ONGOING))
         )
 
-        private fun newActivityInfo() : ActivityInfo {
+        private fun newActivityInfo(): ActivityInfo {
             return ActivityInfo("some package", "some activity")
         }
 
-        private fun newActivityInfoOther() : ActivityInfo {
+        private fun newActivityInfoOther(): ActivityInfo {
             return ActivityInfo("some package 2", "some activity 2")
         }
 
-        private fun newUri() : Uri {
+        private fun newUri(): Uri {
             return Uri.parse("https://www.google.com")
         }
 
-        private fun cloneRequestId(requestId: RequestId) : RequestId {
+        private fun cloneRequestId(requestId: RequestId): RequestId {
             val constructor = requestId::class.java.declaredConstructors[0]
             constructor.isAccessible = true
             return constructor.newInstance(requestId.requestId) as RequestId
@@ -108,7 +108,7 @@
     @Test
     fun testParcelRoundTrip() {
         // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data.
-        val assertParcels = { it : T, data : InputData<T> ->
+        val assertParcels = { it: T, data: InputData<T> ->
             val parcel = Parcel.obtain()
             it.writeToParcel(parcel, 0)
             parcel.setDataPosition(0) // future reads will see all previous writes.
@@ -121,7 +121,7 @@
         assertParcels(inputData.validOther, inputData)
     }
 
-    data class InputData<T : Parcelable>(val valid : T, val validCopy : T, val validOther : T) {
+    data class InputData<T : Parcelable>(val valid: T, val validCopy: T, val validOther: T) {
         val kls = valid.javaClass
         init {
             assertThat(valid).isNotSameAs(validCopy)
@@ -130,8 +130,8 @@
             assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
         }
 
-        fun createFromParcel(parcel : Parcel) : T {
-            val field  = kls.getDeclaredField("CREATOR")
+        fun createFromParcel(parcel: Parcel): T {
+            val field = kls.getDeclaredField("CREATOR")
             val creator = field.get(null) as Parcelable.Creator<T>
 
             return creator.createFromParcel(parcel)
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index ac60e96..22a3cfa 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -43,7 +43,7 @@
     sdk_version: "current",
     data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
     static_libs: [
-        "android-support-test",
+        "androidx.test.rules",
         "guava",
     ],
     manifest: "AndroidManifest.xml",
diff --git a/startop/view_compiler/dex_builder_test/AndroidManifest.xml b/startop/view_compiler/dex_builder_test/AndroidManifest.xml
index 6ac5fc5..b335663 100644
--- a/startop/view_compiler/dex_builder_test/AndroidManifest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.startop.test"
                      android:label="DexBuilder Tests"/>
 
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
index 92e2a71..82509b9 100644
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml
@@ -31,6 +31,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.startop.test" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
 </configuration>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 42d4161..f7b1674 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -14,18 +14,14 @@
 
 package android.startop.test;
 
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import com.google.common.io.ByteStreams;
-import dalvik.system.InMemoryDexClassLoader;
 import dalvik.system.PathClassLoader;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
+
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 // Adding tests here requires changes in several other places. See README.md in
 // the view_compiler directory for more information.
 public class DexBuilderTest {
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
index a3b1b6c..3dfb20c 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -15,18 +15,15 @@
 package android.startop.test;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.view.View;
-import com.google.common.io.ByteStreams;
-import dalvik.system.InMemoryDexClassLoader;
+
+import androidx.test.InstrumentationRegistry;
+
 import dalvik.system.PathClassLoader;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import org.junit.Assert;
+
 import org.junit.Test;
 
+import java.lang.reflect.Method;
+
 // Adding tests here requires changes in several other places. See README.md in
 // the view_compiler directory for more information.
 public class LayoutCompilerTest {
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
index d15e024..745affd 100644
--- a/telecomm/java/android/telecom/CallIdentification.java
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -44,7 +44,7 @@
      * A {@link CallScreeningService} uses this class to create new instances of
      * {@link CallIdentification} for a screened call.
      */
-    public static class Builder {
+    public final static class Builder {
         private CharSequence mName;
         private CharSequence mDescription;
         private CharSequence mDetails;
@@ -67,7 +67,7 @@
          * @param callIdAppName The app name.
          * @hide
          */
-        public Builder(String callIdPackageName, CharSequence callIdAppName) {
+        public Builder(@NonNull String callIdPackageName, @NonNull CharSequence callIdAppName) {
             mPackageName = callIdPackageName;
             mAppName = callIdAppName;
         }
@@ -80,7 +80,7 @@
          * @param name The name associated with the call, or {@code null} if none is provided.
          * @return Builder instance.
          */
-        public Builder setName(@Nullable CharSequence name) {
+        public @NonNull Builder setName(@Nullable CharSequence name) {
             mName = name;
             return this;
         }
@@ -97,7 +97,7 @@
          * @param description The call description, or {@code null} if none is provided.
          * @return Builder instance.
          */
-        public Builder setDescription(@Nullable CharSequence description) {
+        public @NonNull Builder setDescription(@Nullable CharSequence description) {
             mDescription = description;
             return this;
         }
@@ -114,7 +114,8 @@
          * @param details The call details, or {@code null} if none is provided.
          * @return Builder instance.
          */
-        public Builder setDetails(@Nullable CharSequence details) {
+
+        public @NonNull Builder setDetails(@Nullable CharSequence details) {
             mDetails = details;
             return this;
         }
@@ -127,7 +128,7 @@
          * @param photo The photo associated with the call, or {@code null} if none was provided.
          * @return Builder instance.
          */
-        public Builder setPhoto(@Nullable Icon photo) {
+        public @NonNull Builder setPhoto(@Nullable Icon photo) {
             mPhoto = photo;
             return this;
         }
@@ -141,7 +142,7 @@
          * @param nuisanceConfidence The nuisance confidence.
          * @return The builder.
          */
-        public Builder setNuisanceConfidence(@NuisanceConfidence int nuisanceConfidence) {
+        public @NonNull Builder setNuisanceConfidence(@NuisanceConfidence int nuisanceConfidence) {
             mNuisanceConfidence = nuisanceConfidence;
             return this;
         }
@@ -152,7 +153,7 @@
          *
          * @return {@link CallIdentification} instance.
          */
-        public CallIdentification build() {
+        public @NonNull CallIdentification build() {
             return new CallIdentification(mName, mDescription, mDetails, mPhoto,
                     mNuisanceConfidence, mPackageName, mAppName);
         }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d509168..7688ac6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -17,6 +17,7 @@
 import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
@@ -814,10 +815,11 @@
      * <p>
      * The default dialer has access to use this method.
      *
-     * @return The user outgoing phone account selected by the user.
+     * @return The user outgoing phone account selected by the user, or {@code null} if there is no
+     * user selected outgoing {@link PhoneAccountHandle}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
+    public @Nullable PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getUserSelectedOutgoingPhoneAccount(
@@ -833,13 +835,14 @@
      * Sets the user-chosen default {@link PhoneAccountHandle} for making outgoing phone calls.
      *
      * @param accountHandle The {@link PhoneAccountHandle} which will be used by default for making
-     *                      outgoing voice calls.
+     *                      outgoing voice calls, or {@code null} if no default is specified (the
+     *                      user will be asked each time a call is placed in this case).
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @TestApi
     @SystemApi
-    public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+    public void setUserSelectedOutgoingPhoneAccount(@Nullable PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
                 getTelecomService().setUserSelectedOutgoingPhoneAccount(accountHandle);
@@ -1206,7 +1209,8 @@
     /**
      * Used to set the default dialer package.
      *
-     * @param packageName to set the default dialer to.
+     * @param packageName to set the default dialer to, or {@code null} if the system provided
+     *                    dialer should be used instead.
      *
      * @result {@code true} if the default dialer was successfully changed, {@code false} if
      *         the specified package does not correspond to an installed dialer, or is already
@@ -1225,7 +1229,7 @@
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.WRITE_SECURE_SETTINGS})
-    public boolean setDefaultDialer(String packageName) {
+    public boolean setDefaultDialer(@Nullable String packageName) {
         try {
             if (isServiceConnected()) {
                 return getTelecomService().setDefaultDialer(packageName);
@@ -1239,9 +1243,10 @@
     /**
      * Determines the package name of the system-provided default phone app.
      *
-     * @return package name for the system dialer package or null if no system dialer is preloaded.
+     * @return package name for the system dialer package or {@code null} if no system dialer is
+     *         preloaded.
      */
-    public String getSystemDialerPackage() {
+    public @Nullable String getSystemDialerPackage() {
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getSystemDialerPackage();
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 79b7807..9d46300 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -1191,8 +1191,8 @@
              * Broadcast Action: A debug code has been entered in the dialer. This intent is
              * broadcast by the system and OEM telephony apps may need to receive these broadcasts.
              * These "secret codes" are used to activate developer menus by dialing certain codes.
-             * And they are of the form {@code *#*#&lt;code&gt;#*#*}. The intent will have the data
-             * URI: {@code android_secret_code://&lt;code&gt;}. It is possible that a manifest
+             * And they are of the form {@code *#*#<code>#*#*}. The intent will have the data
+             * URI: {@code android_secret_code://<code>}. It is possible that a manifest
              * receiver would be woken up even if it is not currently running.
              *
              * <p>Requires {@code android.Manifest.permission#CONTROL_INCALL_EXPERIENCE} to
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 368f0c6..3a34005 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -29,15 +30,15 @@
  * @hide
  */
 @SystemApi
-public class CallAttributes implements Parcelable {
+public final class CallAttributes implements Parcelable {
     private PreciseCallState mPreciseCallState;
     @NetworkType
     private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
     private CallQuality mCallQuality;
 
 
-    public CallAttributes(PreciseCallState state, @NetworkType int networkType,
-            CallQuality callQuality) {
+    public CallAttributes(@NonNull PreciseCallState state, @NetworkType int networkType,
+            @NonNull CallQuality callQuality) {
         this.mPreciseCallState = state;
         this.mNetworkType = networkType;
         this.mCallQuality = callQuality;
@@ -59,6 +60,7 @@
     /**
      * Returns the {@link PreciseCallState} of the call.
      */
+    @NonNull
     public PreciseCallState getPreciseCallState() {
         return mPreciseCallState;
     }
@@ -96,6 +98,7 @@
     /**
      * Returns the {#link CallQuality} of the call.
      */
+    @NonNull
     public CallQuality getCallQuality() {
         return mCallQuality;
     }
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index 47ce632..78623e7 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -177,7 +177,8 @@
      * @return a list of boolean with the same size as input, indicating if each
      * {@link CarrierIdentifier} is allowed or not.
      */
-    public List<Boolean> isCarrierIdentifiersAllowed(@NonNull List<CarrierIdentifier> carrierIds) {
+    public @NonNull List<Boolean> areCarrierIdentifiersAllowed(
+            @NonNull List<CarrierIdentifier> carrierIds) {
         ArrayList<Boolean> result = new ArrayList<>(carrierIds.size());
 
         // First calculate the result for each slot independently
@@ -332,7 +333,7 @@
     /**
      * Builder for a {@link CarrierRestrictionRules}.
      */
-    public static class Builder {
+    public static final class Builder {
         private final CarrierRestrictionRules mRules;
 
         /** {@hide} */
@@ -341,14 +342,14 @@
         }
 
         /** build command */
-        public CarrierRestrictionRules build() {
+        public @NonNull CarrierRestrictionRules build() {
             return mRules;
         }
 
         /**
          * Indicate that all carriers are allowed.
          */
-        public Builder setAllCarriersAllowed() {
+        public @NonNull Builder setAllCarriersAllowed() {
             mRules.mAllowedCarriers.clear();
             mRules.mExcludedCarriers.clear();
             mRules.mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_ALLOWED;
@@ -360,7 +361,8 @@
          *
          * @param allowedCarriers list of allowed carriers
          */
-        public Builder setAllowedCarriers(List<CarrierIdentifier> allowedCarriers) {
+        public @NonNull Builder setAllowedCarriers(
+                @NonNull List<CarrierIdentifier> allowedCarriers) {
             mRules.mAllowedCarriers = new ArrayList<CarrierIdentifier>(allowedCarriers);
             return this;
         }
@@ -370,7 +372,8 @@
          *
          * @param excludedCarriers list of excluded carriers
          */
-        public Builder setExcludedCarriers(List<CarrierIdentifier> excludedCarriers) {
+        public @NonNull Builder setExcludedCarriers(
+                @NonNull List<CarrierIdentifier> excludedCarriers) {
             mRules.mExcludedCarriers = new ArrayList<CarrierIdentifier>(excludedCarriers);
             return this;
         }
@@ -380,7 +383,7 @@
          *
          * @param carrierRestrictionDefault prioritized carrier list
          */
-        public Builder setDefaultCarrierRestriction(
+        public @NonNull Builder setDefaultCarrierRestriction(
                 @CarrierRestrictionDefault int carrierRestrictionDefault) {
             mRules.mCarrierRestrictionDefault = carrierRestrictionDefault;
             return this;
@@ -391,7 +394,7 @@
          *
          * @param multiSimPolicy multi SIM policy
          */
-        public Builder setMultiSimPolicy(@MultiSimPolicy int multiSimPolicy) {
+        public @NonNull Builder setMultiSimPolicy(@MultiSimPolicy int multiSimPolicy) {
             mRules.mMultiSimPolicy = multiSimPolicy;
             return this;
         }
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index 9614dc5..a9d3079 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -285,6 +285,14 @@
     }
 
     /**
+     * @hide
+     * @return {@code true} if in service.
+     */
+    public boolean isInService() {
+        return mRegState == REG_STATE_HOME || mRegState == REG_STATE_ROAMING;
+    }
+
+    /**
      * Set {@link ServiceState.RoamingType roaming type}. This could override
      * roaming type based on resource overlay or carrier config.
      * @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 79c7195..aaf5f33 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1566,6 +1566,7 @@
      * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
      * available.
      */
+    @Nullable
     public String getTypeAllocationCode() {
         return getTypeAllocationCode(getSlotIndex());
     }
@@ -1576,6 +1577,7 @@
      *
      * @param slotIndex of which Type Allocation Code is returned
      */
+    @Nullable
     public String getTypeAllocationCode(int slotIndex) {
         ITelephony telephony = getITelephony();
         if (telephony == null) return null;
@@ -1636,6 +1638,7 @@
      * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
      * available.
      */
+    @Nullable
     public String getManufacturerCode() {
         return getManufacturerCode(getSlotIndex());
     }
@@ -1646,6 +1649,7 @@
      *
      * @param slotIndex of which Type Allocation Code is returned
      */
+    @Nullable
     public String getManufacturerCode(int slotIndex) {
         ITelephony telephony = getITelephony();
         if (telephony == null) return null;
@@ -2797,8 +2801,8 @@
      * (see {@link #hasCarrierPrivileges}).
      * <p>
      * These "secret codes" are used to activate developer menus by dialing certain codes.
-     * And they are of the form {@code *#*#&lt;code&gt;#*#*}. The intent will have the data
-     * URI: {@code android_secret_code://&lt;code&gt;}. It is possible that a manifest
+     * And they are of the form {@code *#*#<code>#*#*}. The intent will have the data
+     * URI: {@code android_secret_code://<code>}. It is possible that a manifest
      * receiver would be woken up even if it is not currently running.
      * <p>
      * It is supposed to replace {@link android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION}
@@ -6005,6 +6009,7 @@
      * @return IMS Service Table or null if not present or not loaded
      * @hide
      */
+    @Nullable
     @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimIst() {
@@ -10367,12 +10372,6 @@
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void switchMultiSimConfig(int numOfSims) {
-        //only proceed if multi-sim is not restricted
-        if (!isMultisimSupported()) {
-            Rlog.e(TAG, "switchMultiSimConfig not possible. It is restricted or not supported.");
-            return;
-        }
-
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a4ed775..9d072f0 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.hardware.radio.V1_0.ApnTypes;
+import android.hardware.radio.V1_4.ApnTypes;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -79,7 +79,7 @@
      * APN type for all APNs.
      * @hide
      */
-    public static final int TYPE_ALL = ApnTypes.ALL;
+    public static final int TYPE_ALL = ApnTypes.ALL | ApnTypes.MCX;
     /** APN type for default data traffic. */
     public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
     /** APN type for MMS traffic. */
@@ -103,6 +103,8 @@
      * for access to carrier services in an emergency call situation.
      */
     public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+    /** APN type for MCX (Mission Critical Service) where X can be PTT/Video/Data */
+    public static final int TYPE_MCX = ApnTypes.MCX;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "TYPE_" }, value = {
@@ -115,7 +117,8 @@
         TYPE_IMS,
         TYPE_CBS,
         TYPE_IA,
-        TYPE_EMERGENCY
+        TYPE_EMERGENCY,
+        TYPE_MCX
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApnType {}
@@ -206,6 +209,7 @@
         APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
         APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
         APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+        APN_TYPE_STRING_MAP.put("mcx", TYPE_MCX);
         APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
         APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
         APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
@@ -217,6 +221,7 @@
         APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
         APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
         APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+        APN_TYPE_INT_MAP.put(TYPE_MCX, "mcx");
 
         PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
         PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -1833,7 +1838,7 @@
          * {@link ApnSetting} built from this builder otherwise.
          */
         public ApnSetting build() {
-            if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+            if ((mApnTypeBitmask & TYPE_ALL) == 0 || TextUtils.isEmpty(mApnName)
                 || TextUtils.isEmpty(mEntryName)) {
                 return null;
             }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ad34349..afbf46d 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -17,6 +17,7 @@
 
 import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -429,6 +430,7 @@
      *
      * @return an EuiccManager that uses the given card ID for all calls.
      */
+    @NonNull
     public EuiccManager createForCardId(int cardId) {
         return new EuiccManager(mContext, cardId);
     }
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 337375a..a09844d 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.telephony.CallQuality;
@@ -606,7 +607,7 @@
      *
      * @param profile updated ImsStreamMediaProfile
      */
-    public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+    public void callSessionRttAudioIndicatorChanged(@NonNull ImsStreamMediaProfile profile) {
         try {
             mListener.callSessionRttAudioIndicatorChanged(profile);
         } catch (RemoteException e) {
@@ -619,7 +620,7 @@
      *
      * @param callQuality The new call quality
      */
-    public void callQualityChanged(CallQuality callQuality) {
+    public void callQualityChanged(@NonNull CallQuality callQuality) {
         try {
             mListener.callQualityChanged(callQuality);
         } catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 10001bc..464db34 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,6 +25,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Provides STK Call Control Supplementary Service information.
@@ -260,13 +264,13 @@
     public final int result;
 
     private int[] mSsInfo;
-    private ImsCallForwardInfo[] mCfInfo;
-    private ImsSsInfo[] mImsSsInfo;
+    private List<ImsCallForwardInfo> mCfInfo;
+    private List<ImsSsInfo> mImsSsInfo;
 
     /**
      * Builder for optional ImsSsData parameters.
      */
-    public static class Builder {
+    public static final class Builder {
         private ImsSsData mImsSsData;
 
         /**
@@ -301,7 +305,7 @@
          * Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
          * data.
          */
-        public @NonNull Builder setSuppServiceInfo(@NonNull ImsSsInfo[] imsSsInfos) {
+        public @NonNull Builder setSuppServiceInfo(@NonNull List<ImsSsInfo> imsSsInfos) {
             mImsSsData.mImsSsInfo = imsSsInfos;
             return this;
         }
@@ -311,7 +315,7 @@
          * service data.
          */
         public @NonNull Builder setCallForwardingInfo(
-                @NonNull ImsCallForwardInfo[] imsCallForwardInfos) {
+                @NonNull List<ImsCallForwardInfo> imsCallForwardInfos) {
             mImsSsData.mCfInfo = imsCallForwardInfos;
             return this;
         }
@@ -360,8 +364,8 @@
         serviceClass = in.readInt();
         result = in.readInt();
         mSsInfo = in.createIntArray();
-        mCfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
-        mImsSsInfo = (ImsSsInfo[])in.readParcelableArray(this.getClass().getClassLoader());
+        mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
+        mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
     }
 
     public static final @android.annotation.NonNull Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
@@ -384,8 +388,8 @@
         out.writeInt(getServiceClass());
         out.writeInt(getResult());
         out.writeIntArray(mSsInfo);
-        out.writeParcelableArray(mCfInfo, 0);
-        out.writeParcelableArray(mImsSsInfo, 0);
+        out.writeParcelableList(mCfInfo, 0);
+        out.writeParcelableList(mImsSsInfo, 0);
     }
 
     @Override
@@ -500,12 +504,12 @@
 
     /** @hide */
     public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
-        mImsSsInfo = imsSsInfo;
+        mImsSsInfo = Arrays.asList(imsSsInfo);
     }
 
     /** @hide */
     public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
-        mCfInfo = cfInfo;
+        mCfInfo = Arrays.asList(cfInfo);
     }
 
     /**
@@ -524,7 +528,7 @@
 
 
         int[] result = new int[2];
-        if (mImsSsInfo == null || mImsSsInfo.length == 0) {
+        if (mImsSsInfo == null || mImsSsInfo.size() == 0) {
             Rlog.e(TAG, "getSuppServiceInfoCompat: Could not parse mImsSsInfo, returning empty "
                     + "int[]");
             return result;
@@ -535,26 +539,26 @@
         if (isTypeClir()) {
             // Assume there will only be one ImsSsInfo.
             // contains {"n","m"} parameters
-            result[0] = mImsSsInfo[0].getClirOutgoingState();
-            result[1] = mImsSsInfo[0].getClirInterrogationStatus();
+            result[0] = mImsSsInfo.get(0).getClirOutgoingState();
+            result[1] = mImsSsInfo.get(0).getClirInterrogationStatus();
             return result;
         }
         // COLR 7.31
         if (isTypeColr()) {
-            result[0] = mImsSsInfo[0].getProvisionStatus();
+            result[0] = mImsSsInfo.get(0).getProvisionStatus();
         }
         // Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
         // other result, just return the status for the "n" parameter and provisioning status for
         // "m" as the default.
-        result[0] = mImsSsInfo[0].getStatus();
-        result[1] = mImsSsInfo[0].getProvisionStatus();
+        result[0] = mImsSsInfo.get(0).getStatus();
+        result[1] = mImsSsInfo.get(0).getProvisionStatus();
         return result;
     }
 
     /**
      * @return an array of {@link ImsSsInfo}s associated with this supplementary service data.
      */
-    public @NonNull ImsSsInfo[] getSuppServiceInfo() {
+    public @NonNull List<ImsSsInfo> getSuppServiceInfo() {
         return mImsSsInfo;
     }
 
@@ -562,7 +566,7 @@
      * @return an array of {@link ImsCallForwardInfo}s associated with this supplementary service
      * data.
      **/
-    public ImsCallForwardInfo[] getCallForwardInfo() {
+    public @Nullable List<ImsCallForwardInfo> getCallForwardInfo() {
         return mCfInfo;
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 303a9fe..18e7530 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -173,7 +173,7 @@
     /**
      * Builds {@link ImsSsInfo} instances, which may include optional parameters.
      */
-    public static class Builder {
+    public static final class Builder {
 
         private final ImsSsInfo mImsSsInfo;
 
@@ -304,7 +304,7 @@
     /**
      * @return The Incoming Communication Barring (ICB) number.
      */
-    public String getIncomingCommunicationBarringNumber() {
+    public @Nullable String getIncomingCommunicationBarringNumber() {
         return mIcbNum;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index e87d28c..d5061a3 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -141,6 +141,8 @@
     /** APN type for Emergency PDN. This is not an IA apn, but is used
      * for access to carrier services in an emergency call situation. */
     public static final String APN_TYPE_EMERGENCY = "emergency";
+    /** APN type for Mission Critical Services */
+    public static final String APN_TYPE_MCX = "mcx";
     /** Array of all APN types */
     public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
             APN_TYPE_MMS,
@@ -151,7 +153,8 @@
             APN_TYPE_IMS,
             APN_TYPE_CBS,
             APN_TYPE_IA,
-            APN_TYPE_EMERGENCY
+            APN_TYPE_EMERGENCY,
+            APN_TYPE_MCX
     };
 
     public static final int RIL_CARD_MAX_APPS    = 8;
diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk
index f23a665..e1f56b8 100644
--- a/tests/ActivityManagerPerfTests/tests/Android.mk
+++ b/tests/ActivityManagerPerfTests/tests/Android.mk
@@ -21,7 +21,7 @@
     $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     apct-perftests-utils \
     ActivityManagerPerfTestsUtils
 
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
index a1ab33a..04aef47 100644
--- a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
@@ -25,6 +25,6 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.frameworks.perftests.amtests"/>
 </manifest>
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
index ffb5404..76c40b2 100644
--- a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
+++ b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
@@ -24,6 +24,6 @@
     <option name="test-tag" value="ActivityManagerPerfTests"/>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.frameworks.perftests.amtests"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
     </test>
 </configuration>
\ No newline at end of file
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
index 58fb136..daff76f 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
@@ -21,7 +21,8 @@
 import android.content.ServiceConnection;
 import android.perftests.utils.ManualBenchmarkState;
 import android.perftests.utils.PerfManualStatusReporter;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import com.android.frameworks.perftests.am.util.TargetPackageUtils;
 import com.android.frameworks.perftests.am.util.TimeReceiver;
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
index f7dab03..bc528d4 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
@@ -17,8 +17,9 @@
 package com.android.frameworks.perftests.am.tests;
 
 import android.content.Intent;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.perftests.am.util.Constants;
 
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
index 3bf56ce..8e82219 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java
@@ -17,8 +17,9 @@
 package com.android.frameworks.perftests.am.tests;
 
 import android.content.ContentProviderClient;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.perftests.am.util.TargetPackageUtils;
 
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java
index e1263db..996c5a5 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java
@@ -21,8 +21,9 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.IBinder;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.perftests.am.util.Constants;
 import com.android.frameworks.perftests.am.util.TargetPackageUtils;
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
index f05f323..ba20640 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
@@ -19,8 +19,9 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.perftests.am.util.Constants;
 import com.android.frameworks.perftests.am.util.TargetPackageUtils;
diff --git a/tests/ActivityManagerPerfTests/utils/Android.mk b/tests/ActivityManagerPerfTests/utils/Android.mk
index 60c9423..7a7471d 100644
--- a/tests/ActivityManagerPerfTests/utils/Android.mk
+++ b/tests/ActivityManagerPerfTests/utils/Android.mk
@@ -23,7 +23,7 @@
     src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     junit \
     ub-uiautomator
 
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
index 67071d2..fc787ba 100644
--- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
@@ -19,10 +19,11 @@
 import android.content.Intent;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.io.IOException;
 
 public class Utils {
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index 1fb548b..f50bca5 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -12,7 +12,7 @@
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 5ea8ff1..9d7319f 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -30,11 +30,12 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.support.test.rule.logging.AtraceLogger;
 import android.test.InstrumentationTestCase;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
 
+import androidx.test.rule.logging.AtraceLogger;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
@@ -51,6 +52,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
 /**
  * This test is intended to measure the time it takes for the apps to start.
  * Names of the applications are passed in command line, and the
diff --git a/tests/AppLaunchWear/Android.mk b/tests/AppLaunchWear/Android.mk
index 6d08366..332b680 100644
--- a/tests/AppLaunchWear/Android.mk
+++ b/tests/AppLaunchWear/Android.mk
@@ -12,7 +12,7 @@
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
index d36d84e..97701c6 100644
--- a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
@@ -30,16 +30,16 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.support.test.rule.logging.AtraceLogger;
 import android.test.InstrumentationTestCase;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
 
+import androidx.test.rule.logging.AtraceLogger;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -52,6 +52,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
 /**
  * This test is intended to measure the time it takes for the apps to start.
  * Names of the applications are passed in command line, and the
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/Android.mk b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
index b10305d..f47cf96 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
+++ b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
 
 LOCAL_PACKAGE_NAME := BackgroundDexOptServiceIntegrationTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml
index afae155..aec9f77 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml
+++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml
@@ -34,7 +34,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.bgdexopttest"
         android:label="Integration test for BackgroundDexOptService" />
 </manifest>
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml
index 9bb1e28..a532422 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml
+++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml
@@ -50,6 +50,6 @@
     <option name="test-tag" value="BackgroundDexOptServiceIntegrationTests"/>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.frameworks.bgdexopttest"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
     </test>
 </configuration>
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
index fd20f4a..7d826f7 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
+++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
@@ -17,17 +17,16 @@
 package com.android.server.pm;
 
 import android.app.AlarmManager;
-import android.app.UiAutomation;
 import android.content.Context;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.os.storage.StorageManager;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
diff --git a/tests/Camera2Tests/CameraToo/tests/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.mk
index eb8f6c3..fe4dc42 100644
--- a/tests/Camera2Tests/CameraToo/tests/Android.mk
+++ b/tests/Camera2Tests/CameraToo/tests/Android.mk
@@ -20,6 +20,6 @@
 LOCAL_INSTRUMENTATION_FOR := CameraToo
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules mockito-target-minus-junit4
 
 include $(BUILD_PACKAGE)
diff --git a/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml
index 30210ba..8d35749 100644
--- a/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml
+++ b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application android:label="CameraToo">
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.example.android.camera2.cameratoo"
         android:label="CameraToo tests" />
 </manifest>
diff --git a/tests/Compatibility/Android.mk b/tests/Compatibility/Android.mk
index 9c47a26..643f9eb 100644
--- a/tests/Compatibility/Android.mk
+++ b/tests/Compatibility/Android.mk
@@ -17,7 +17,7 @@
 
 # We only want this apk build for tests.
 LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 # Include all test java files.
 LOCAL_SRC_FILES := \
 	$(call all-java-files-under, src)
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index 95eb5c9..d683ec7 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -33,10 +33,11 @@
 import android.os.DropBoxManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java
index b61ec34..960ea49 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibilityRunner.java
@@ -16,7 +16,7 @@
 
 package com.android.compatibilitytest;
 
-import android.support.test.runner.AndroidJUnitRunner;
+import androidx.test.runner.AndroidJUnitRunner;
 
 // empty subclass to maintain backwards compatibility on host-side harness
 public class AppCompatibilityRunner extends AndroidJUnitRunner {}
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
index f324eb1..62c1ba8 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
@@ -67,7 +67,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     truth-prebuilt \
 
 # Include both versions of the .so if we have 2 arch
diff --git a/tests/DynamicCodeLoggerIntegrationTests/AndroidManifest.xml b/tests/DynamicCodeLoggerIntegrationTests/AndroidManifest.xml
index 4327da2..08fac30 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/AndroidManifest.xml
+++ b/tests/DynamicCodeLoggerIntegrationTests/AndroidManifest.xml
@@ -29,7 +29,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.dynamiccodeloggertest"
         android:label="Integration test for DynamicCodeLogger" />
 </manifest>
diff --git a/tests/DynamicCodeLoggerIntegrationTests/AndroidTest.xml b/tests/DynamicCodeLoggerIntegrationTests/AndroidTest.xml
index f70b9c8..f8a1ec9 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/AndroidTest.xml
+++ b/tests/DynamicCodeLoggerIntegrationTests/AndroidTest.xml
@@ -24,7 +24,7 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.frameworks.dynamiccodeloggertest"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
index 4f9aeea..db2f659 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
+++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
@@ -24,11 +24,12 @@
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
 import android.util.EventLog;
 import android.util.EventLog.Event;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import dalvik.system.DexClassLoader;
 
 import org.junit.Before;
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index ba63940..5b1a36b 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -29,7 +29,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.server.wm.flicker"
                      android:label="WindowManager Flicker Tests">
     </instrumentation>
diff --git a/tests/FlickerTests/lib/Android.mk b/tests/FlickerTests/lib/Android.mk
index 6a8dfe8..e438822 100644
--- a/tests/FlickerTests/lib/Android.mk
+++ b/tests/FlickerTests/lib/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_STATIC_JAVA_LIBRARIES := \
-   ub-janktesthelper \
+   androidx.test.janktesthelper \
    cts-amwm-util \
    platformprotosnano \
    layersprotosnano \
@@ -41,7 +41,7 @@
     src/com/android/server/wm/flicker/WindowUtils.java
 LOCAL_STATIC_JAVA_LIBRARIES := sysui-helper \
     launcher-helper-lib \
-    compatibility-device-util
+    compatibility-device-util-axt
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
index 6306f0e..e00a247 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
@@ -30,7 +30,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.launcherhelper.LauncherStrategyFactory;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
@@ -43,6 +42,8 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 
+import androidx.test.InstrumentationRegistry;
+
 /**
  * Collection of UI Automation helper functions.
  */
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
index f6e8192..0a3fe3c 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
@@ -18,9 +18,10 @@
 
 import android.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.server.wm.flicker.monitor.ITransitionMonitor;
 import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
 import com.android.server.wm.flicker.monitor.ScreenRecorder;
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
index 0da8761..c54396f 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
@@ -20,10 +20,11 @@
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
 import android.view.Surface;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+
 /**
  * Helper functions to retrieve system window sizes and positions.
  */
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
index 717d187..3f86f0d 100644
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
@@ -25,7 +25,7 @@
 /**
  * Monitors {@link android.view.WindowAnimationFrameStats} to detect janky frames.
  *
- * Adapted from {@link android.support.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
+ * Adapted from {@link androidx.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
  * using the same threshold to determine jank.
  */
 public class WindowAnimationFrameStatsMonitor implements ITransitionMonitor {
diff --git a/tests/FlickerTests/lib/test/Android.mk b/tests/FlickerTests/lib/test/Android.mk
index 0e3f58d..5be89ba 100644
--- a/tests/FlickerTests/lib/test/Android.mk
+++ b/tests/FlickerTests/lib/test/Android.mk
@@ -25,7 +25,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     platform-test-annotations \
     truth-prebuilt \
     platformprotosnano \
diff --git a/tests/FlickerTests/lib/test/AndroidManifest.xml b/tests/FlickerTests/lib/test/AndroidManifest.xml
index d30172d..6451a57 100644
--- a/tests/FlickerTests/lib/test/AndroidManifest.xml
+++ b/tests/FlickerTests/lib/test/AndroidManifest.xml
@@ -18,7 +18,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.server.wm.flicker"
                      android:label="WindowManager Flicker Lib Test">
     </instrumentation>
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
index 42b2aca..7d77126 100644
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
@@ -26,9 +26,10 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Test;
 
 import java.util.List;
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
index 5a24e6d..c46175c 100644
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
@@ -17,7 +17,8 @@
 package com.android.server.wm.flicker;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import com.google.common.io.ByteStreams;
 
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
index f7fa0d5..dd6fed0 100644
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
@@ -18,9 +18,7 @@
 
 import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Ignore;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
index 34f4ebb..b6860cb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -25,11 +25,12 @@
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
 import android.util.Log;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
index 2b62fcf..6590b86 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
@@ -20,9 +20,10 @@
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
 import android.platform.helpers.IAppHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
index 42b161f..4771b02 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -20,9 +20,10 @@
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
 import android.platform.helpers.IAppHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
index 92bb1ea..65888ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -30,7 +30,6 @@
 import android.content.Intent;
 import android.os.RemoteException;
 import android.platform.helpers.IAppHelper;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
@@ -38,6 +37,8 @@
 import android.util.Rational;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
 
 /**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
index fec248c..61cca0d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -17,12 +17,13 @@
 package com.android.server.wm.flicker;
 
 import android.platform.helpers.IAppHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.util.Rational;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
index 7061b23..00e11c0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -21,10 +21,11 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.platform.helpers.IAppHelper;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
 
 import org.junit.After;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
index 7e71369..7818c4e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -20,9 +20,9 @@
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
index 745569a..63018ec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -19,9 +19,9 @@
 import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
index de7639d..1aba930 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -20,9 +20,9 @@
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
index 1bd519c..a81fa8e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
@@ -19,8 +19,8 @@
 import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
index 8a15cbd..50dba81 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -25,11 +25,12 @@
 
 import android.graphics.Rect;
 import android.platform.helpers.IAppHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Rational;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
index 3eab68d..117ac5a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
@@ -27,10 +27,11 @@
 
 import android.content.Intent;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
index 40bd4e9..1d30df9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -19,10 +19,10 @@
 import static com.android.server.wm.flicker.CommonTransitions.splitScreenToLauncher;
 import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/Internal/Android.mk b/tests/Internal/Android.mk
index da56696..2e26ef1 100644
--- a/tests/Internal/Android.mk
+++ b/tests/Internal/Android.mk
@@ -11,7 +11,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_STATIC_JAVA_LIBRARIES := junit \
-    android-support-test \
+    androidx.test.rules \
     mockito-target-minus-junit4
 
 LOCAL_JAVA_RESOURCE_DIRS := res
diff --git a/tests/Internal/AndroidManifest.xml b/tests/Internal/AndroidManifest.xml
index e5a5694..c85c3b1 100644
--- a/tests/Internal/AndroidManifest.xml
+++ b/tests/Internal/AndroidManifest.xml
@@ -38,7 +38,7 @@
         </service>
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.internal.tests"
                      android:label="Internal Tests" />
 </manifest>
diff --git a/tests/Internal/AndroidTest.xml b/tests/Internal/AndroidTest.xml
index 6531c93..7b67e9e 100644
--- a/tests/Internal/AndroidTest.xml
+++ b/tests/Internal/AndroidTest.xml
@@ -24,6 +24,6 @@
     <option name="test-tag" value="InternalTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.internal.tests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
 </configuration>
\ No newline at end of file
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index 881f628..65ff6eb 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -20,8 +20,9 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Assert;
 import org.junit.Test;
diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java
index 7f06f2c..476b991 100644
--- a/tests/Internal/src/android/app/WallpaperInfoTest.java
+++ b/tests/Internal/src/android/app/WallpaperInfoTest.java
@@ -26,9 +26,10 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.service.wallpaper.WallpaperService;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index b9e282e..592aa3a 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
index 39608a4..17fa931 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
@@ -27,9 +27,10 @@
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Color;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.colorextraction.types.ExtractionType;
diff --git a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
index a7d5ae8..d92cfce 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
@@ -20,11 +20,12 @@
 
 import android.app.WallpaperColors;
 import android.graphics.Color;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Range;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.graphics.ColorUtils;
 
diff --git a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
index 73df9a0..d0bb8e3 100644
--- a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
+++ b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
@@ -16,13 +16,14 @@
 
 package com.android.internal.graphics;
 
+import static org.junit.Assert.assertTrue;
+
 import android.graphics.Color;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
-import static org.junit.Assert.assertTrue;
-
 @SmallTest
 public class ColorUtilsTest {
 
diff --git a/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java b/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java
index a64f8a6..540a1ec 100644
--- a/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java
+++ b/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.annotation.SuppressLint;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Assert;
 import org.junit.Before;
diff --git a/tests/PackageWatchdog/Android.mk b/tests/PackageWatchdog/Android.mk
index 1c1c2a4..1e4aacc 100644
--- a/tests/PackageWatchdog/Android.mk
+++ b/tests/PackageWatchdog/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     junit \
     frameworks-base-testutils \
-    android-support-test \
+    androidx.test.rules \
     services.core
 
 LOCAL_JAVA_LIBRARIES := \
diff --git a/tests/PackageWatchdog/AndroidManifest.xml b/tests/PackageWatchdog/AndroidManifest.xml
index fa89528..540edb4 100644
--- a/tests/PackageWatchdog/AndroidManifest.xml
+++ b/tests/PackageWatchdog/AndroidManifest.xml
@@ -22,7 +22,7 @@
     </application>
 
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.tests.packagewatchdog"
                      android:label="PackageWatchdog Test"/>
 </manifest>
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 77cd62e..598f60a 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -24,7 +24,8 @@
 
 import android.content.pm.VersionedPackage;
 import android.os.test.TestLooper;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
diff --git a/tests/RcsTests/Android.mk b/tests/RcsTests/Android.mk
index 7b348d7..a276584 100644
--- a/tests/RcsTests/Android.mk
+++ b/tests/RcsTests/Android.mk
@@ -11,7 +11,7 @@
 
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4 truth-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := junit androidx.test.rules mockito-target-minus-junit4 truth-prebuilt
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/RcsTests/AndroidManifest.xml b/tests/RcsTests/AndroidManifest.xml
index a7e7d47..b1706a0 100644
--- a/tests/RcsTests/AndroidManifest.xml
+++ b/tests/RcsTests/AndroidManifest.xml
@@ -6,6 +6,6 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.tests.rcs"/>
 </manifest>
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
index 89d32ab..e898942 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
@@ -19,10 +19,11 @@
 
 import android.net.Uri;
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsGroupThreadIconChangedEvent;
 import android.telephony.ims.RcsGroupThreadIconChangedEventDescriptor;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
index 726b9cd..356688d 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
@@ -18,10 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsGroupThreadNameChangedEvent;
 import android.telephony.ims.RcsGroupThreadNameChangedEventDescriptor;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
index a109310..572fcb8 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
@@ -18,10 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
 import android.telephony.ims.RcsGroupThreadParticipantJoinedEventDescriptor;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
index de2688c..038b2e8 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
@@ -18,10 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
 import android.telephony.ims.RcsGroupThreadParticipantLeftEventDescriptor;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
index 5724054..283c71b 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
@@ -18,10 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsParticipantAliasChangedEvent;
 import android.telephony.ims.RcsParticipantAliasChangedEventDescriptor;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
index 6361a39..2d95513 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
@@ -18,9 +18,10 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsParticipantQueryParams;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
index beb4f8a..fb51bda 100644
--- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
@@ -21,10 +21,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsParticipant;
 import android.telephony.ims.RcsThreadQueryParams;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index db9376b..206f867 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -107,7 +107,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src)
 LOCAL_PACKAGE_NAME := RollbackTest
 LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_COMPATIBILITY_SUITE := general-tests
 LOCAL_JAVA_RESOURCE_FILES := \
   $(ROLLBACK_TEST_APP_AV1) \
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index ac39f85..70cd867 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -20,7 +20,7 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.tests.rollback" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
 
         <!-- Exclude the StagedRollbackTest tests, which needs to be specially
              driven from the StagedRollbackTest host test -->
diff --git a/tests/RollbackTest/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
index e57a768..5380dc9 100644
--- a/tests/RollbackTest/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
@@ -24,7 +24,7 @@
     </application>
 
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.tests.rollback"
                      android:label="Rollback Test"/>
 
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
index ddcf1da..267ef73 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java
@@ -21,7 +21,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
index e10f866..8a925b9 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java
@@ -20,9 +20,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index a6054e8..4780f30 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -21,6 +21,11 @@
 import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
 import static com.android.tests.rollback.RollbackTestUtils.processUserData;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
@@ -31,13 +36,9 @@
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.provider.DeviceConfig;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 2f989f3..e8cbd60 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -16,6 +16,11 @@
 
 package com.android.tests.rollback;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,13 +35,9 @@
 import android.content.rollback.RollbackManager;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
+import androidx.test.InstrumentationRegistry;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/tests/ServiceCrashTest/Android.mk b/tests/ServiceCrashTest/Android.mk
index d1f6450..617ee7c 100644
--- a/tests/ServiceCrashTest/Android.mk
+++ b/tests/ServiceCrashTest/Android.mk
@@ -12,7 +12,7 @@
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.base
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt androidx.test.rules
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/UsageStatsPerfTests/Android.mk b/tests/UsageStatsPerfTests/Android.mk
index cd29b51..4304fb0 100644
--- a/tests/UsageStatsPerfTests/Android.mk
+++ b/tests/UsageStatsPerfTests/Android.mk
@@ -21,7 +21,7 @@
     $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     apct-perftests-utils \
     services.usage
 
diff --git a/tests/UsageStatsPerfTests/AndroidManifest.xml b/tests/UsageStatsPerfTests/AndroidManifest.xml
index 596a79c..9891548 100644
--- a/tests/UsageStatsPerfTests/AndroidManifest.xml
+++ b/tests/UsageStatsPerfTests/AndroidManifest.xml
@@ -24,6 +24,6 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.frameworks.perftests.usage"/>
 </manifest>
diff --git a/tests/UsageStatsPerfTests/AndroidTest.xml b/tests/UsageStatsPerfTests/AndroidTest.xml
index c9b51dc..b8892eb 100644
--- a/tests/UsageStatsPerfTests/AndroidTest.xml
+++ b/tests/UsageStatsPerfTests/AndroidTest.xml
@@ -23,6 +23,6 @@
     <option name="test-tag" value="UsageStatsPerfTests"/>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.frameworks.perftests.usage"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
     </test>
 </configuration>
\ No newline at end of file
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index e2a4c26..7d9d0d5 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -24,9 +24,10 @@
 import android.os.SystemClock;
 import android.perftests.utils.ManualBenchmarkState;
 import android.perftests.utils.PerfManualStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.usage.IntervalStats;
 import com.android.server.usage.UsageStatsDatabase;
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 0105893..adcd11a 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -41,6 +41,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -196,8 +197,8 @@
                     intent.setPackage(getPackageName());
                     intent.putExtra(EXTRA_KEY_TIMEOUT, true);
                     mUsageStatsManager.registerAppUsageLimitObserver(1, packages,
-                            60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
-                                    1, intent, 0));
+                            Duration.ofSeconds(60), Duration.ofSeconds(60),
+                            PendingIntent.getActivity(UsageStatsActivity.this, 1, intent, 0));
                 }
             }
         });
diff --git a/tests/UsbTests/Android.mk b/tests/UsbTests/Android.mk
index 4e215cc..aef993b 100644
--- a/tests/UsbTests/Android.mk
+++ b/tests/UsbTests/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     frameworks-base-testutils \
-    android-support-test \
+    androidx.test.rules \
     mockito-target-inline-minus-junit4 \
     platform-test-annotations \
     services.core \
diff --git a/tests/UsbTests/AndroidManifest.xml b/tests/UsbTests/AndroidManifest.xml
index 5d60695..03d1a3e 100644
--- a/tests/UsbTests/AndroidManifest.xml
+++ b/tests/UsbTests/AndroidManifest.xml
@@ -24,7 +24,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.server.usb"
                      android:label="UsbTests"/>
 </manifest>
diff --git a/tests/UsbTests/AndroidTest.xml b/tests/UsbTests/AndroidTest.xml
index 4affad3..e55bc98 100644
--- a/tests/UsbTests/AndroidTest.xml
+++ b/tests/UsbTests/AndroidTest.xml
@@ -24,7 +24,7 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.server.usb"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
index ea027d7..89dc79c 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
@@ -16,29 +16,29 @@
 
 package com.android.server.usb;
 
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.usb.descriptors.UsbDescriptorParser;
-import com.android.server.usb.descriptors.UsbDeviceDescriptor;
-import com.google.common.io.ByteStreams;
 
-import java.io.InputStream;
-import java.io.IOException;
-import java.lang.Exception;
+import com.google.common.io.ByteStreams;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
+import java.io.InputStream;
+
 /**
  * Tests for {@link com.android.server.usb.descriptors.UsbDescriptorParser}
  */
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index 4b93ca3..ca1eb70 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -31,11 +31,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.UserHandle;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.FgThread;
 
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
index 7800a80..1c2d167 100644
--- a/tests/WindowAnimationJank/Android.mk
+++ b/tests/WindowAnimationJank/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ub-uiautomator \
-    ub-janktesthelper \
+    androidx.test.janktesthelper \
     junit
 
 LOCAL_JAVA_LIBRARIES := android.test.base.stubs
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
index 1fb502a..6792a8b 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/FullscreenRotationTest.java
@@ -17,8 +17,9 @@
 package android.windowanimationjank;
 
 import android.os.Bundle;
-import android.support.test.jank.JankTest;
-import android.support.test.jank.GfxMonitor;
+
+import androidx.test.jank.GfxMonitor;
+import androidx.test.jank.JankTest;
 
 /**
  * Detect janks during screen rotation for full-screen activity. Periodically change
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
index bf739fa..a8ace162 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -16,12 +16,10 @@
 
 package android.windowanimationjank;
 
-import java.io.IOException;
-import java.util.StringTokenizer;
-
-import android.support.test.jank.JankTestBase;
 import android.support.test.uiautomator.UiDevice;
 
+import androidx.test.jank.JankTestBase;
+
 /**
  * This adds additional system level jank monitor and its result is merged with primary monitor
  * used in test.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1a0e8fa..fbc1a65 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -939,11 +939,19 @@
             return mConnected;  // Similar trickery
         }
 
-        public void connect() {
+        private void connect(boolean isAlwaysMetered) {
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mConnected = true;
             mConfig = new VpnConfig();
-            mConfig.isMetered = false;
+            mConfig.isMetered = isAlwaysMetered;
+        }
+
+        public void connectAsAlwaysMetered() {
+            connect(true /* isAlwaysMetered */);
+        }
+
+        public void connect() {
+            connect(false /* isAlwaysMetered */);
         }
 
         @Override
@@ -5104,6 +5112,202 @@
     }
 
     @Test
+    public void testIsActiveNetworkMeteredOverWifi() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+
+        assertFalse(mCm.isActiveNetworkMetered());
+    }
+
+    @Test
+    public void testIsActiveNetworkMeteredOverCell() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        mCellNetworkAgent.connect(true);
+        waitForIdle();
+
+        assertTrue(mCm.isActiveNetworkMetered());
+    }
+
+    @Test
+    public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        mCellNetworkAgent.connect(true);
+        waitForIdle();
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // Connect VPN network. By default it is using current default network (Cell).
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        final int uid = Process.myUid();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true);
+        mMockVpn.connect();
+        waitForIdle();
+        // Ensure VPN is now the active network.
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // Expect VPN to be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // Connect WiFi.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        // VPN should still be the active network.
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // Expect VPN to be unmetered as it should now be using WiFi (new default).
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Disconnecting Cell should not affect VPN's meteredness.
+        mCellNetworkAgent.disconnect();
+        waitForIdle();
+
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Disconnect WiFi; Now there is no platform default network.
+        mWiFiNetworkAgent.disconnect();
+        waitForIdle();
+
+        // VPN without any underlying networks is treated as metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
+    }
+
+   @Test
+   public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        mCellNetworkAgent.connect(true);
+        waitForIdle();
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Connect VPN network.
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        final int uid = Process.myUid();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true);
+        mMockVpn.connect();
+        waitForIdle();
+        // Ensure VPN is now the active network.
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        // VPN is using Cell
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mCellNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Expect VPN to be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN is now using WiFi
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mWiFiNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Expect VPN to be unmetered
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // VPN is using Cell | WiFi.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Expect VPN to be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN is using WiFi | Cell.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Order should not matter and VPN should still be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN is not using any underlying networks.
+        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        waitForIdle();
+
+        // VPN without underlying networks is treated as metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
+    }
+
+    @Test
+    public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Connect VPN network.
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        final int uid = Process.myUid();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true);
+        mMockVpn.connectAsAlwaysMetered();
+        waitForIdle();
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // VPN is tracking current platform default (WiFi).
+        mService.setUnderlyingNetworksForVpn(null);
+        waitForIdle();
+
+        // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN explicitly declares WiFi as its underlying network.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mWiFiNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Doesn't really matter whether VPN declares its underlying networks explicitly.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // With WiFi lost, VPN is basically without any underlying networks. And in that case it is
+        // anyways suppose to be metered.
+        mWiFiNetworkAgent.disconnect();
+        waitForIdle();
+
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        vpnNetworkAgent.disconnect();
+    }
+
+    @Test
     public void testNetworkBlockedStatus() {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
index 6f58893..ee97628 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -23,6 +23,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Optional;
 
@@ -86,20 +87,98 @@
     }
 
     /**
+     * Represents a way to access a property, either a getter or a field.
+     */
+    public static final class Accessor {
+        private final String mName;
+        private final Type mType;
+
+        /**
+         * Construct an accessor for a field.
+         *
+         * @param name The name of the field
+         * @return The new accessor
+         * @see Type#FIELD
+         */
+        static Accessor ofField(String name) {
+            return new Accessor(name, Type.FIELD);
+        }
+
+        /**
+         * Construct an accessor for a getter.
+         *
+         * @param name The name of the getter
+         * @return The new accessor
+         * @see Type#GETTER
+         */
+        static Accessor ofGetter(String name) {
+            return new Accessor(name, Type.GETTER);
+        }
+
+        public Accessor(String name, Type type) {
+            mName = Objects.requireNonNull(name, "Accessor name must not be null");
+            mType = Objects.requireNonNull(type, "Accessor type must not be null");
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public Type getType() {
+            return mType;
+        }
+
+        /**
+         * Get the invocation of this accessor.
+         *
+         * Example: {@code "getValue()"} for a getter or {@code "valueField"} for a field.
+         *
+         * @return A string representing the invocation of this accessor
+         */
+        public String invocation() {
+            switch (mType) {
+                case FIELD:
+                    return mName;
+                case GETTER:
+                    return String.format("%s()", mName);
+                default:
+                    throw new NoSuchElementException(
+                            String.format("No such accessor type %s", mType));
+            }
+        }
+
+        public enum Type {
+            /**
+             * A property accessed by a public field.
+             *
+             * @see #ofField(String)
+             */
+            FIELD,
+
+            /**
+             * A property accessed by a public getter method.
+             *
+             * @see #ofGetter(String)
+             */
+            GETTER
+        }
+    }
+
+    /**
      * Model an inspectable property
      */
     public static final class Property {
         private final String mName;
-        private final String mGetter;
+        private final Accessor mAccessor;
         private final Type mType;
         private boolean mAttributeIdInferrableFromR = true;
         private int mAttributeId = 0;
         private List<IntEnumEntry> mIntEnumEntries;
         private List<IntFlagEntry> mIntFlagEntries;
 
-        public Property(String name, String getter, Type type) {
+        public Property(String name, Accessor accessor, Type type) {
             mName = Objects.requireNonNull(name, "Name must not be null");
-            mGetter = Objects.requireNonNull(getter, "Getter must not be null");
+            mAccessor = Objects.requireNonNull(accessor, "Accessor must not be null");
             mType = Objects.requireNonNull(type, "Type must not be null");
         }
 
@@ -129,8 +208,8 @@
             return mName;
         }
 
-        public String getGetter() {
-            return mGetter;
+        public Accessor getAccessor() {
+            return mAccessor;
         }
 
         public Type getType() {
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
index 50c79da..6305228 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
@@ -16,6 +16,7 @@
 
 package android.processor.view.inspector;
 
+import android.processor.view.inspector.InspectableClassModel.Accessor;
 import android.processor.view.inspector.InspectableClassModel.IntEnumEntry;
 import android.processor.view.inspector.InspectableClassModel.IntFlagEntry;
 import android.processor.view.inspector.InspectableClassModel.Property;
@@ -93,16 +94,15 @@
         try {
             final AnnotationMirror annotation =
                     mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
-            final ExecutableElement getter = ensureGetter(element);
-            final Property property = buildProperty(getter, annotation);
+            final Property property = buildProperty(element, annotation);
 
             model.getProperty(property.getName()).ifPresent(p -> {
                 throw new ProcessingException(
                         String.format(
-                                "Property \"%s\" is already defined on #%s().",
+                                "Property \"%s\" is already defined on #%s.",
                                 p.getName(),
-                                p.getGetter()),
-                        getter,
+                                p.getAccessor().invocation()),
+                        element,
                         annotation);
             });
 
@@ -112,6 +112,95 @@
         }
     }
 
+
+    /**
+     * Build a {@link Property} from a getter and an inspectable property annotation.
+     *
+     * @param accessor An element representing the getter or public field to build from
+     * @param annotation A mirror of an inspectable property-shaped annotation
+     * @return A property for the getter and annotation
+     * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled
+     */
+    private Property buildProperty(Element accessor, AnnotationMirror annotation) {
+        final Property property;
+        final Optional<String> nameFromAnnotation = mAnnotationUtils
+                .typedValueByName("name", String.class, accessor, annotation);
+
+        validateModifiers(accessor);
+
+        switch (accessor.getKind()) {
+            case FIELD:
+                property = new Property(
+                        nameFromAnnotation.orElseGet(() -> accessor.getSimpleName().toString()),
+                        Accessor.ofField(accessor.getSimpleName().toString()),
+                        determinePropertyType(accessor, annotation));
+                break;
+            case METHOD:
+                final ExecutableElement getter = ensureGetter(accessor);
+
+                property = new Property(
+                        nameFromAnnotation.orElseGet(() -> inferPropertyNameFromGetter(getter)),
+                        Accessor.ofGetter(getter.getSimpleName().toString()),
+                        determinePropertyType(getter, annotation));
+                break;
+            default:
+                throw new ProcessingException(
+                        String.format(
+                                "Property must either be a getter method or a field, got %s.",
+                                accessor.getKind()
+                        ),
+                        accessor,
+                        annotation);
+        }
+
+        mAnnotationUtils
+                .typedValueByName("hasAttributeId", Boolean.class, accessor, annotation)
+                .ifPresent(property::setAttributeIdInferrableFromR);
+
+        mAnnotationUtils
+                .typedValueByName("attributeId", Integer.class, accessor, annotation)
+                .ifPresent(property::setAttributeId);
+
+        switch (property.getType()) {
+            case INT_ENUM:
+                property.setIntEnumEntries(processEnumMapping(accessor, annotation));
+                break;
+            case INT_FLAG:
+                property.setIntFlagEntries(processFlagMapping(accessor, annotation));
+                break;
+        }
+
+        return property;
+    }
+
+    /**
+     * Validates that an element is public, concrete, and non-static.
+     *
+     * @param element The element to check
+     * @throws ProcessingException If the element's modifiers are invalid
+     */
+    private void validateModifiers(Element element) {
+        final Set<Modifier> modifiers = element.getModifiers();
+
+        if (!modifiers.contains(Modifier.PUBLIC)) {
+            throw new ProcessingException(
+                    "Property getter methods and fields must be public.",
+                    element);
+        }
+
+        if (modifiers.contains(Modifier.ABSTRACT)) {
+            throw new ProcessingException(
+                    "Property getter methods must not be abstract.",
+                    element);
+        }
+
+        if (modifiers.contains(Modifier.STATIC)) {
+            throw new ProcessingException(
+                    "Property getter methods and fields must not be static.",
+                    element);
+        }
+    }
+
     /**
      * Check that an element is shaped like a getter.
      *
@@ -127,25 +216,7 @@
         }
 
         final ExecutableElement method = (ExecutableElement) element;
-        final Set<Modifier> modifiers = method.getModifiers();
 
-        if (modifiers.contains(Modifier.PRIVATE)) {
-            throw new ProcessingException(
-                    "Property getter methods must not be private.",
-                    element);
-        }
-
-        if (modifiers.contains(Modifier.ABSTRACT)) {
-            throw new ProcessingException(
-                    "Property getter methods must not be abstract.",
-                    element);
-        }
-
-        if (modifiers.contains(Modifier.STATIC)) {
-            throw new ProcessingException(
-                    "Property getter methods must not be static.",
-                    element);
-        }
 
         if (!method.getParameters().isEmpty()) {
             throw new ProcessingException(
@@ -171,77 +242,41 @@
         return method;
     }
 
-    /**
-     * Build a {@link Property} from a getter and an inspectable property annotation.
-     *
-     * @param getter     An element representing the getter to build from
-     * @param annotation A mirror of an inspectable property-shaped annotation
-     * @return A property for the getter and annotation
-     * @throws ProcessingException If the supplied data is invalid and a property cannot be modeled
-     */
-    private Property buildProperty(ExecutableElement getter, AnnotationMirror annotation) {
-        final String name = mAnnotationUtils
-                .typedValueByName("name", String.class, getter, annotation)
-                .orElseGet(() -> inferPropertyNameFromGetter(getter));
-
-        final Property property = new Property(
-                name,
-                getter.getSimpleName().toString(),
-                determinePropertyType(getter, annotation));
-
-        mAnnotationUtils
-                .typedValueByName("hasAttributeId", Boolean.class, getter, annotation)
-                .ifPresent(property::setAttributeIdInferrableFromR);
-
-        mAnnotationUtils
-                .typedValueByName("attributeId", Integer.class, getter, annotation)
-                .ifPresent(property::setAttributeId);
-
-        switch (property.getType()) {
-            case INT_ENUM:
-                property.setIntEnumEntries(processEnumMapping(getter, annotation));
-                break;
-            case INT_FLAG:
-                property.setIntFlagEntries(processFlagMapping(getter, annotation));
-                break;
-        }
-
-        return property;
-    }
 
     /**
      * Determine the property type from the annotation, return type, or context clues.
      *
-     * @param getter     An element representing the getter to build from
+     * @param accessor An element representing the getter or field to determine the type of
      * @param annotation A mirror of an inspectable property-shaped annotation
      * @return The resolved property type
      * @throws ProcessingException If the property type cannot be resolved or is invalid
      * @see android.view.inspector.InspectableProperty#valueType()
      */
     private Property.Type determinePropertyType(
-            ExecutableElement getter,
+            Element accessor,
             AnnotationMirror annotation) {
 
         final String valueType = mAnnotationUtils
-                .untypedValueByName("valueType", getter, annotation)
+                .untypedValueByName("valueType", accessor, annotation)
                 .map(Object::toString)
                 .orElse("INFERRED");
 
-        final Property.Type returnType = convertReturnTypeToPropertyType(getter);
+        final Property.Type accessorType =
+                convertTypeMirrorToPropertyType(extractReturnOrFieldType(accessor), accessor);
 
-        final boolean hasColor = hasColorAnnotation(getter);
+        final boolean hasColor = hasColorAnnotation(accessor);
         final Optional<AnnotationValue> enumMapping =
                 mAnnotationUtils.valueByName("enumMapping", annotation);
         final Optional<AnnotationValue> flagMapping =
                 mAnnotationUtils.valueByName("flagMapping", annotation);
 
-        if (returnType != Property.Type.INT) {
+        if (accessorType != Property.Type.INT) {
             enumMapping.ifPresent(value -> {
                 throw new ProcessingException(
                         String.format(
                                 "Can only use enumMapping on int types, got %s.",
-                                returnType.toString().toLowerCase()),
-                        getter,
+                                accessorType.toString().toLowerCase()),
+                        accessor,
                         annotation,
                         value);
             });
@@ -249,8 +284,8 @@
                 throw new ProcessingException(
                         String.format(
                                 "Can only use flagMapping on int types, got %s.",
-                                returnType.toString().toLowerCase()),
-                        getter,
+                                accessorType.toString().toLowerCase()),
+                        accessor,
                         annotation,
                         value);
             });
@@ -262,14 +297,14 @@
                     enumMapping.ifPresent(value -> {
                         throw new ProcessingException(
                                 "Cannot use enumMapping on a color type.",
-                                getter,
+                                accessor,
                                 annotation,
                                 value);
                     });
                     flagMapping.ifPresent(value -> {
                         throw new ProcessingException(
                                 "Cannot use flagMapping on a color type.",
-                                getter,
+                                accessor,
                                 annotation,
                                 value);
                     });
@@ -278,7 +313,7 @@
                     flagMapping.ifPresent(value -> {
                         throw new ProcessingException(
                                 "Cannot use flagMapping and enumMapping simultaneously.",
-                                getter,
+                                accessor,
                                 annotation,
                                 value);
                     });
@@ -286,12 +321,12 @@
                 } else if (flagMapping.isPresent()) {
                     return Property.Type.INT_FLAG;
                 } else {
-                    return returnType;
+                    return accessorType;
                 }
             case "NONE":
-                return returnType;
+                return accessorType;
             case "COLOR":
-                switch (returnType) {
+                switch (accessorType) {
                     case COLOR:
                     case INT:
                     case LONG:
@@ -299,37 +334,58 @@
                     default:
                         throw new ProcessingException(
                                 "Color must be a long, integer, or android.graphics.Color",
-                                getter,
+                                accessor,
                                 annotation);
                 }
             case "GRAVITY":
-                requirePackedIntToReturnInt("Gravity", returnType, getter, annotation);
+                requirePackedIntToBeInt("Gravity", accessorType, accessor, annotation);
                 return Property.Type.GRAVITY;
             case "INT_ENUM":
-                requirePackedIntToReturnInt("IntEnum", returnType, getter, annotation);
+                requirePackedIntToBeInt("IntEnum", accessorType, accessor, annotation);
                 return Property.Type.INT_ENUM;
             case "INT_FLAG":
-                requirePackedIntToReturnInt("IntFlag", returnType, getter, annotation);
+                requirePackedIntToBeInt("IntFlag", accessorType, accessor, annotation);
                 return Property.Type.INT_FLAG;
             default:
                 throw new ProcessingException(
                         String.format("Unknown value type enumeration value: %s", valueType),
-                        getter,
+                        accessor,
                         annotation);
         }
     }
 
     /**
-     * Get a property type from the return type of a getter.
+     * Get the type of a field or the return type of a method.
      *
-     * @param getter The getter to extract the return type of
+     * @param element The element to extract a {@link TypeMirror} from
+     * @return The return or field type of the element
+     * @throws ProcessingException If the element is not a field or a method
+     */
+    private TypeMirror extractReturnOrFieldType(Element element) {
+        switch (element.getKind()) {
+            case FIELD:
+                return element.asType();
+            case METHOD:
+                return ((ExecutableElement) element).getReturnType();
+            default:
+                throw new ProcessingException(
+                        String.format(
+                                "Unable to determine the type of a %s.",
+                                element.getKind()),
+                        element);
+        }
+    }
+
+    /**
+     * Get a property type from a type mirror
+     *
+     * @param typeMirror The type mirror to convert to a property type
+     * @param element The element to be used for exceptions
      * @return The property type returned by the getter
      * @throws ProcessingException If the return type is not a primitive or an object
      */
-    private Property.Type convertReturnTypeToPropertyType(ExecutableElement getter) {
-        final TypeMirror returnType = getter.getReturnType();
-
-        switch (unboxType(returnType)) {
+    private Property.Type convertTypeMirrorToPropertyType(TypeMirror typeMirror, Element element) {
+        switch (unboxType(typeMirror)) {
             case BOOLEAN:
                 return Property.Type.BOOLEAN;
             case BYTE:
@@ -347,7 +403,7 @@
             case SHORT:
                 return Property.Type.SHORT;
             case DECLARED:
-                if (isColorType(returnType)) {
+                if (isColorType(typeMirror)) {
                     return Property.Type.COLOR;
                 } else {
                     return Property.Type.OBJECT;
@@ -356,24 +412,24 @@
                 return Property.Type.OBJECT;
             default:
                 throw new ProcessingException(
-                        String.format("Unsupported return type %s.", returnType),
-                        getter);
+                        String.format("Unsupported property type %s.", typeMirror),
+                        element);
         }
     }
 
     /**
      * Require that a value type packed into an integer be on a getter that returns an int.
      *
-     * @param typeName   The name of the type to use in the exception
+     * @param typeName The name of the type to use in the exception
      * @param returnType The return type of the getter to check
-     * @param getter     The getter, to use in the exception
+     * @param accessor The getter, to use in the exception
      * @param annotation The annotation, to use in the exception
      * @throws ProcessingException If the return type is not an int
      */
-    private static void requirePackedIntToReturnInt(
+    private static void requirePackedIntToBeInt(
             String typeName,
             Property.Type returnType,
-            ExecutableElement getter,
+            Element accessor,
             AnnotationMirror annotation) {
         if (returnType != Property.Type.INT) {
             throw new ProcessingException(
@@ -381,7 +437,7 @@
                             "%s can only be defined on a method that returns int, got %s.",
                             typeName,
                             returnType.toString().toLowerCase()),
-                    getter,
+                    accessor,
                     annotation);
         }
     }
@@ -393,21 +449,21 @@
      * not considered to be annotated, nor is a {@code long} annotated with
      * {@link android.annotation.ColorInt}.
      *
-     * @param getter The getter to query
+     * @param accessor The getter or field to query
      * @return True if the getter has a color annotation, false otherwise
      */
-    private boolean hasColorAnnotation(ExecutableElement getter) {
-        switch (unboxType(getter.getReturnType())) {
+    private boolean hasColorAnnotation(Element accessor) {
+        switch (unboxType(extractReturnOrFieldType(accessor))) {
             case INT:
                 for (String name : COLOR_INT_ANNOTATION_NAMES) {
-                    if (mAnnotationUtils.hasAnnotation(getter, name)) {
+                    if (mAnnotationUtils.hasAnnotation(accessor, name)) {
                         return true;
                     }
                 }
                 return false;
             case LONG:
                 for (String name : COLOR_LONG_ANNOTATION_NAMES) {
-                    if (mAnnotationUtils.hasAnnotation(getter, name)) {
+                    if (mAnnotationUtils.hasAnnotation(accessor, name)) {
                         return true;
                     }
                 }
@@ -452,40 +508,40 @@
      *
      * @see android.view.inspector.IntEnumMapping
      * @see android.view.inspector.InspectableProperty#enumMapping()
-     * @param getter The getter of the property, used for exceptions
+     * @param accessor The accessor of the property, used for exceptions
      * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to
      *                   extract enum mapping values from.
      * @return A list of int enum entries, in the order specified in source
      * @throws ProcessingException if mapping doesn't exist or is invalid
      */
     private List<IntEnumEntry> processEnumMapping(
-            ExecutableElement getter,
+            Element accessor,
             AnnotationMirror annotation) {
         List<AnnotationMirror> enumAnnotations = mAnnotationUtils.typedArrayValuesByName(
-                "enumMapping", AnnotationMirror.class, getter, annotation);
+                "enumMapping", AnnotationMirror.class, accessor, annotation);
         List<IntEnumEntry> enumEntries = new ArrayList<>(enumAnnotations.size());
 
         if (enumAnnotations.isEmpty()) {
             throw new ProcessingException(
-                    "Encountered an empty array for enumMapping", getter, annotation);
+                    "Encountered an empty array for enumMapping", accessor, annotation);
         }
 
         for (AnnotationMirror enumAnnotation : enumAnnotations) {
             final String name = mAnnotationUtils.typedValueByName(
-                    "name", String.class, getter, enumAnnotation)
+                    "name", String.class, accessor, enumAnnotation)
                     .orElseThrow(() -> {
                         throw new ProcessingException(
                                 "Name is required for @EnumMap",
-                                getter,
+                                accessor,
                                 enumAnnotation);
                     });
 
             final int value = mAnnotationUtils.typedValueByName(
-                    "value", Integer.class, getter, enumAnnotation)
+                    "value", Integer.class, accessor, enumAnnotation)
                     .orElseThrow(() -> {
                         throw new ProcessingException(
                                 "Value is required for @EnumMap",
-                                getter,
+                                accessor,
                                 enumAnnotation);
                     });
 
@@ -504,45 +560,45 @@
      *
      * @see android.view.inspector.IntFlagMapping
      * @see android.view.inspector.InspectableProperty#flagMapping()
-     * @param getter The getter of the property, used for exceptions
+     * @param accessor The accessor of the property, used for exceptions
      * @param annotation The {@link android.view.inspector.InspectableProperty} annotation to
      *                   extract flag mapping values from.
      * @return A list of int flags entries, in the order specified in source
      * @throws ProcessingException if mapping doesn't exist or is invalid
      */
     private List<IntFlagEntry> processFlagMapping(
-            ExecutableElement getter,
+            Element accessor,
             AnnotationMirror annotation) {
         List<AnnotationMirror> flagAnnotations = mAnnotationUtils.typedArrayValuesByName(
-                "flagMapping", AnnotationMirror.class, getter, annotation);
+                "flagMapping", AnnotationMirror.class, accessor, annotation);
         List<IntFlagEntry> flagEntries = new ArrayList<>(flagAnnotations.size());
 
         if (flagAnnotations.isEmpty()) {
             throw new ProcessingException(
-                    "Encountered an empty array for flagMapping", getter, annotation);
+                    "Encountered an empty array for flagMapping", accessor, annotation);
         }
 
         for (AnnotationMirror flagAnnotation : flagAnnotations) {
             final String name = mAnnotationUtils.typedValueByName(
-                    "name", String.class, getter, flagAnnotation)
+                    "name", String.class, accessor, flagAnnotation)
                     .orElseThrow(() -> {
                         throw new ProcessingException(
                                 "Name is required for @FlagMap",
-                                getter,
+                                accessor,
                                 flagAnnotation);
                     });
 
             final int target = mAnnotationUtils.typedValueByName(
-                    "target", Integer.class, getter, flagAnnotation)
+                    "target", Integer.class, accessor, flagAnnotation)
                     .orElseThrow(() -> {
                         throw new ProcessingException(
                                 "Target is required for @FlagMap",
-                                getter,
+                                accessor,
                                 flagAnnotation);
                     });
 
             final Optional<Integer> mask = mAnnotationUtils.typedValueByName(
-                    "mask", Integer.class, getter, flagAnnotation);
+                    "mask", Integer.class, accessor, flagAnnotation);
 
             if (mask.isPresent()) {
                 flagEntries.add(new IntFlagEntry(name, target, mask.get()));
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
index 7b04645..1ab9914 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -16,6 +16,7 @@
 
 package android.processor.view.inspector;
 
+
 import android.processor.view.inspector.InspectableClassModel.IntEnumEntry;
 import android.processor.view.inspector.InspectableClassModel.IntFlagEntry;
 import android.processor.view.inspector.InspectableClassModel.Property;
@@ -268,10 +269,10 @@
 
         for (PropertyIdField propertyIdField : propertyIdFields) {
             builder.addStatement(
-                    "propertyReader.read$L($N, node.$L())",
+                    "propertyReader.read$L($N, node.$L)",
                     methodSuffixForPropertyType(propertyIdField.mProperty.getType()),
                     propertyIdField.mFieldSpec,
-                    propertyIdField.mProperty.getGetter());
+                    propertyIdField.mProperty.getAccessor().invocation());
         }
 
         return builder.build();
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
index f6d8bb0..35f0fcf 100644
--- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -20,6 +20,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
+
+import android.processor.view.inspector.InspectableClassModel.Accessor;
 import android.processor.view.inspector.InspectableClassModel.IntEnumEntry;
 import android.processor.view.inspector.InspectableClassModel.IntFlagEntry;
 import android.processor.view.inspector.InspectableClassModel.Property;
@@ -86,7 +88,7 @@
 
     @Test
     public void testNoAttributeId() {
-        final Property property = new Property(
+        final Property property = addProperty(
                 "noAttributeProperty",
                 "getNoAttributeProperty",
                 Property.Type.INT);
@@ -98,19 +100,18 @@
 
     @Test
     public void testSuppliedAttributeId() {
-        final Property property = new Property(
+        final Property property = addProperty(
                 "suppliedAttributeProperty",
                 "getSuppliedAttributeProperty",
                 Property.Type.INT);
         property.setAttributeId(0xdecafbad);
-        mModel.putProperty(property);
 
         assertGeneratedFileEquals("SuppliedAttributeId");
     }
 
     @Test
     public void testIntEnum() {
-        final Property property = new Property(
+        final Property property = addProperty(
                 "intEnumProperty",
                 "getIntEnumProperty",
                 Property.Type.INT_ENUM);
@@ -127,7 +128,7 @@
 
     @Test
     public void testIntFlag() {
-        final Property property = new Property(
+        final Property property = addProperty(
                 "intFlag",
                 "getIntFlag",
                 Property.Type.INT_FLAG);
@@ -139,13 +140,21 @@
                 new IntFlagEntry("WARP", 0x4)
         ));
 
-        mModel.putProperty(property);
-
         assertGeneratedFileEquals("IntFlag");
     }
 
+    @Test
+    public void testFieldProperty() {
+        mModel.putProperty(new Property(
+                "fieldProperty",
+                Accessor.ofField("fieldProperty"),
+                Property.Type.INT));
+
+        assertGeneratedFileEquals("FieldProperty");
+    }
+
     private Property addProperty(String name, String getter, Property.Type type) {
-        final Property property = new Property(name, getter, type);
+        final Property property = new Property(name, Accessor.ofGetter(getter), type);
         mModel.putProperty(property);
         return property;
     }
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/FieldProperty.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/FieldProperty.java.txt
new file mode 100644
index 0000000..a44c43e
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/FieldProperty.java.txt
@@ -0,0 +1,39 @@
+package com.android.node;
+
+import android.R;
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link TestNode}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestNode$$InspectionCompanion implements InspectionCompanion<TestNode> {
+    /**
+     * Set by {@link #mapProperties(PropertyMapper)} once properties have been mapped.
+     */
+    private boolean mPropertiesMapped = false;
+
+    /**
+     * Property ID of {@code fieldProperty}.
+     */
+    private int mFieldPropertyId;
+
+    @Override
+    public void mapProperties(PropertyMapper propertyMapper) {
+        mFieldPropertyId = propertyMapper.mapInt("fieldProperty", R.attr.fieldProperty);
+        mPropertiesMapped = true;
+    }
+
+    @Override
+    public void readProperties(TestNode node, PropertyReader propertyReader) {
+        if (!mPropertiesMapped) {
+            throw new InspectionCompanion.UninitializedPropertyMapException();
+        }
+        propertyReader.readInt(mFieldPropertyId, node.fieldProperty);
+    }
+}