Merge "Add a carrier config for PROVISIONING button in radioinfo" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bff222e..c61f7c6 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -437,10 +437,6 @@
     name: "hwbinder-stubs-docs",
     srcs: [
         "core/java/android/os/HidlSupport.java",
-        "core/java/android/annotation/IntDef.java",
-        "core/java/android/annotation/IntRange.java",
-        "core/java/android/annotation/NonNull.java",
-        "core/java/android/annotation/SystemApi.java",
         "core/java/android/os/HidlMemory.java",
         "core/java/android/os/HwBinder.java",
         "core/java/android/os/HwBlob.java",
@@ -466,6 +462,7 @@
 java_library_static {
     name: "hwbinder.stubs",
     sdk_version: "core_current",
+    libs: ["stub-annotations"],
     srcs: [
         ":hwbinder-stubs-docs",
     ],
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 3d129d8..20ce133 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -135,6 +135,10 @@
         ":updatable-media-srcs",
     ],
 
+    api_lint: {
+        enabled: false,
+    },
+
     libs: [
         "framework_media_annotation",
     ],
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index 6158e2e..1d912eb 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -3,38 +3,19 @@
 
   public final class MediaTranscodeManager {
     method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener);
-    field public static final int PRIORITY_REALTIME = 1; // 0x1
-    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
   }
 
   @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
     method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession);
   }
 
-  public static final class MediaTranscodeManager.TranscodingRequest {
+  public abstract static class MediaTranscodeManager.TranscodingRequest {
     method public int getClientPid();
     method public int getClientUid();
     method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor();
     method @NonNull public android.net.Uri getDestinationUri();
-    method public int getPriority();
     method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor();
     method @NonNull public android.net.Uri getSourceUri();
-    method public int getType();
-    method @Nullable public android.media.MediaFormat getVideoTrackFormat();
-  }
-
-  public static final class MediaTranscodeManager.TranscodingRequest.Builder {
-    ctor public MediaTranscodeManager.TranscodingRequest.Builder();
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
-    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
   }
 
   public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
@@ -71,5 +52,18 @@
     method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int);
   }
 
+  public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest {
+    method @NonNull public android.media.MediaFormat getVideoTrackFormat();
+  }
+
+  public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder {
+    ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
+    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build();
+    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int);
+    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int);
+    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(android.os.ParcelFileDescriptor);
+    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(android.os.ParcelFileDescriptor);
+  }
+
 }
 
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 84332e5..79e0d58 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -50,27 +50,23 @@
 import java.util.concurrent.Executors;
 
 /**
- MediaTranscodeManager provides an interface to the system's media transcoding service and can be
- used to transcode media files, e.g. transcoding a video from HEVC to AVC.
+ Android 12 introduces Compatible media transcoding feature.  See
+ <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
+ Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media
+ transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to
+ AVC.
 
  <h3>Transcoding Types</h3>
  <h4>Video Transcoding</h4>
- When transcoding a video file, the video file could be of any of the following types:
- <ul>
- <li> Video file with single video track. </li>
- <li> Video file with multiple video track. </li>
- <li> Video file with multiple video tracks and audio tracks. </li>
- <li> Video file with video/audio tracks and metadata track. Note that metadata track will be passed
- through only if it could be recognized by {@link MediaExtractor}.
- TODO(hkuang): Finalize the metadata track behavior. </li>
- </ul>
+ When transcoding a video file, the video track will be transcoded based on the desired track format
+ and the audio track will be pass through without any modification.
  <p class=note>
- Note that currently only support transcoding video file in mp4 format.
+ Note that currently only support transcoding video file in mp4 format and with single video track.
 
  <h3>Transcoding Request</h3>
  <p>
  To transcode a media file, first create a {@link TranscodingRequest} through its builder class
- {@link TranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
+ {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
  {@link MediaTranscodeManager#enqueueRequest(
          TranscodingRequest, Executor, OnTranscodingFinishedListener)}
  TranscodeRequest are processed based on client process's priority and request priority. When a
@@ -82,23 +78,9 @@
  Here is an example where <code>Builder</code> is used to specify all parameters
 
  <pre class=prettyprint>
- TranscodingRequest request =
-    new TranscodingRequest.Builder()
-        .setSourceUri(srcUri)
-        .setDestinationUri(dstUri)
-        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-        .setPriority(REALTIME)
-        .setVideoTrackFormat(videoFormat)
-        .build();
+ VideoTranscodingRequest request =
+    new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build();
  }</pre>
-
- TODO(hkuang): Add architecture diagram showing the transcoding service and api.
- TODO(hkuang): Add sample code when API is settled.
- TODO(hkuang): Clarify whether multiple video tracks is supported or not.
- TODO(hkuang): Clarify whether image/audio transcoding is supported or not.
- TODO(hkuang): Clarify what will happen if there is unrecognized track in the source.
- TODO(hkuang): Clarify whether supports scaling.
- TODO(hkuang): Clarify whether supports framerate conversion.
  @hide
  */
 @MinSdk(Build.VERSION_CODES.S)
@@ -116,68 +98,6 @@
     private static final float BPP = 0.25f;
 
     /**
-     * Default transcoding type.
-     * @hide
-     */
-    public static final int TRANSCODING_TYPE_UNKNOWN = 0;
-
-    /**
-     * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video file.
-     * <p>Note that currently only support transcoding video file in mp4 format.
-     */
-    public static final int TRANSCODING_TYPE_VIDEO = 1;
-
-    /**
-     * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image file.
-     * @hide
-     */
-    public static final int TRANSCODING_TYPE_IMAGE = 2;
-
-    /** @hide */
-    @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
-            TRANSCODING_TYPE_UNKNOWN,
-            TRANSCODING_TYPE_VIDEO,
-            TRANSCODING_TYPE_IMAGE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TranscodingType {}
-
-    /**
-     * Default value.
-     * @hide
-     */
-    public static final int PRIORITY_UNKNOWN = 0;
-    /**
-     * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
-     * client wants the transcoding result as soon as possible.
-     * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
-     * performance penalty due to resource reallocation to prioritize the sessions with higher
-     * priority.
-     * TODO(hkuang): Add more description of this when priority is finalized.
-     */
-    public static final int PRIORITY_REALTIME = 1;
-
-    /**
-     * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not need
-     * the transcoding result as soon as possible.
-     * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to
-     * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
-     * delay of the transcoding result.
-     * @hide
-     * TODO(hkuang): Add more description of this when priority is finalized.
-     */
-    public static final int PRIORITY_OFFLINE = 2;
-
-    /** @hide */
-    @IntDef(prefix = {"PRIORITY_"}, value = {
-            PRIORITY_UNKNOWN,
-            PRIORITY_REALTIME,
-            PRIORITY_OFFLINE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TranscodingPriority {}
-
-    /**
      * Listener that gets notified when a transcoding operation has finished.
      * This listener gets notified regardless of how the operation finished. It is up to the
      * listener implementation to check the result and take appropriate action.
@@ -503,7 +423,79 @@
         }
     }
 
-    public static final class TranscodingRequest {
+    /**
+     * Abstract base class for all the TranscodingRequest.
+     * <p> TranscodingRequest encapsulates the desired configuration for the transcoding.
+     */
+    public abstract static class TranscodingRequest {
+        /**
+         *
+         * Default transcoding type.
+         * @hide
+         */
+        public static final int TRANSCODING_TYPE_UNKNOWN = 0;
+
+        /**
+         * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video.
+         * <p>Note that currently only support transcoding video file in mp4 format.
+         * @hide
+         */
+        public static final int TRANSCODING_TYPE_VIDEO = 1;
+
+        /**
+         * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image.
+         * @hide
+         */
+        public static final int TRANSCODING_TYPE_IMAGE = 2;
+
+        /** @hide */
+        @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
+                TRANSCODING_TYPE_UNKNOWN,
+                TRANSCODING_TYPE_VIDEO,
+                TRANSCODING_TYPE_IMAGE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface TranscodingType {}
+
+        /**
+         * Default value.
+         *
+         * @hide
+         */
+        public static final int PRIORITY_UNKNOWN = 0;
+        /**
+         * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
+         * client wants the transcoding result as soon as possible.
+         * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
+         * performance penalty due to resource reallocation to prioritize the sessions with higher
+         * priority.
+         *
+         * @hide
+         */
+        public static final int PRIORITY_REALTIME = 1;
+
+        /**
+         * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not
+         * need the transcoding result as soon as possible.
+         * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set
+         * to
+         * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
+         * delay of the transcoding result.
+         *
+         * @hide
+         *
+         */
+        public static final int PRIORITY_OFFLINE = 2;
+
+        /** @hide */
+        @IntDef(prefix = {"PRIORITY_"}, value = {
+                PRIORITY_UNKNOWN,
+                PRIORITY_REALTIME,
+                PRIORITY_OFFLINE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface TranscodingPriority {}
+
         /** Uri of the source media file. */
         private @NonNull Uri mSourceUri;
 
@@ -537,22 +529,6 @@
         private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
 
         /**
-         * Desired output video format of the destination file.
-         * <p> If this is null, source file's video track will be passed through and copied to the
-         * destination file.
-         * <p>
-         */
-        private @Nullable MediaFormat mVideoTrackFormat = null;
-
-        /**
-         * Desired output audio format of the destination file.
-         * <p> If this is null, source file's audio track will be passed through and copied to the
-         * destination file.
-         * @hide
-         */
-        private @Nullable MediaFormat mAudioTrackFormat = null;
-
-        /**
          * Desired image format for the destination file.
          * <p> If this is null, source file's image track will be passed through and copied to the
          * destination file.
@@ -563,6 +539,12 @@
         @VisibleForTesting
         private TranscodingTestConfig mTestConfig = null;
 
+        /**
+         * Prevent public constructor access.
+         */
+        /* package private */ TranscodingRequest() {
+        }
+
         private TranscodingRequest(Builder b) {
             mSourceUri = b.mSourceUri;
             mSourceFileDescriptor = b.mSourceFileDescriptor;
@@ -572,13 +554,13 @@
             mClientPid = b.mClientPid;
             mPriority = b.mPriority;
             mType = b.mType;
-            mVideoTrackFormat = b.mVideoTrackFormat;
-            mAudioTrackFormat = b.mAudioTrackFormat;
-            mImageFormat = b.mImageFormat;
             mTestConfig = b.mTestConfig;
         }
 
-        /** Return the type of the transcoding. */
+        /**
+         * Return the type of the transcoding.
+         * @hide
+         */
         @TranscodingType
         public int getType() {
             return mType;
@@ -624,22 +606,16 @@
             return mDestinationFileDescriptor;
         }
 
-        /** Return priority of the transcoding. */
+        /**
+         * Return priority of the transcoding.
+         * @hide
+         */
         @TranscodingPriority
         public int getPriority() {
             return mPriority;
         }
 
         /**
-         * Return the video track format of the transcoding.
-         * This will be null is the transcoding is not for video transcoding.
-         */
-        @Nullable
-        public MediaFormat getVideoTrackFormat() {
-            return mVideoTrackFormat;
-        }
-
-        /**
          * Return TestConfig of the transcoding.
          * @hide
          */
@@ -648,6 +624,8 @@
             return mTestConfig;
         }
 
+        abstract void writeFormatToParcel(TranscodingRequestParcel parcel);
+
         /* Writes the TranscodingRequest to a parcel. */
         private TranscodingRequestParcel writeToParcel(@NonNull Context context) {
             TranscodingRequestParcel parcel = new TranscodingRequestParcel();
@@ -671,7 +649,7 @@
                 }
                 parcel.clientPackageName = packageName;
             }
-            parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
+            writeFormatToParcel(parcel);
             if (mTestConfig != null) {
                 parcel.isForTesting = true;
                 parcel.testConfig = mTestConfig;
@@ -679,71 +657,12 @@
             return parcel;
         }
 
-        /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
-        private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
-            if (format == null) {
-                throw new IllegalArgumentException("Invalid MediaFormat");
-            }
-
-            TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
-
-            if (format.containsKey(MediaFormat.KEY_MIME)) {
-                String mime = format.getString(MediaFormat.KEY_MIME);
-                if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
-                    trackFormat.codecType = TranscodingVideoCodecType.kAvc;
-                } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
-                    trackFormat.codecType = TranscodingVideoCodecType.kHevc;
-                } else {
-                    throw new UnsupportedOperationException("Only support transcode to avc/hevc");
-                }
-            }
-
-            if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
-                int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
-                if (bitrateBps <= 0) {
-                    throw new IllegalArgumentException("Bitrate must be larger than 0");
-                }
-                trackFormat.bitrateBps = bitrateBps;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
-                    MediaFormat.KEY_HEIGHT)) {
-                int width = format.getInteger(MediaFormat.KEY_WIDTH);
-                int height = format.getInteger(MediaFormat.KEY_HEIGHT);
-                if (width <= 0 || height <= 0) {
-                    throw new IllegalArgumentException("Width and height must be larger than 0");
-                }
-                // TODO(hkuang): Validate the aspect ratio after adding scaling.
-                trackFormat.width = width;
-                trackFormat.height = height;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_PROFILE)) {
-                int profile = format.getInteger(MediaFormat.KEY_PROFILE);
-                if (profile <= 0) {
-                    throw new IllegalArgumentException("Invalid codec profile");
-                }
-                // TODO(hkuang): Validate the profile according to codec type.
-                trackFormat.profile = profile;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_LEVEL)) {
-                int level = format.getInteger(MediaFormat.KEY_LEVEL);
-                if (level <= 0) {
-                    throw new IllegalArgumentException("Invalid codec level");
-                }
-                // TODO(hkuang): Validate the level according to codec type.
-                trackFormat.level = level;
-            }
-
-            return trackFormat;
-        }
-
         /**
-         * Builder class for {@link TranscodingRequest} objects.
-         * Use this class to configure and create a <code>TranscodingRequest</code> instance.
+         * Builder to build a {@link TranscodingRequest} object.
+         *
+         * @param <T> The subclass to be built.
          */
-        public static final class Builder {
+        abstract static class Builder<T extends Builder<T>> {
             private @NonNull Uri mSourceUri;
             private @NonNull Uri mDestinationUri;
             private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null;
@@ -752,80 +671,68 @@
             private int mClientPid = -1;
             private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
             private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
-            private @Nullable MediaFormat mVideoTrackFormat;
-            private @Nullable MediaFormat mAudioTrackFormat;
-            private @Nullable MediaFormat mImageFormat;
             private TranscodingTestConfig mTestConfig;
 
+            abstract T self();
+
             /**
-             * Specifies the uri of source media file.
+             * Creates a builder for building {@link TranscodingRequest}s.
              *
              * Client must set the source Uri. If client also provides the source fileDescriptor
              * through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)},
              * TranscodingSession will use the fd instead of calling back to the client to open the
              * sourceUri.
+             *
+             *
+             * @param type The transcoding type.
              * @param sourceUri Content uri for the source media file.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if Uri is null or empty.
+             * @param destinationUri Content uri for the destination media file.
+             *
              */
-            @NonNull
-            public Builder setSourceUri(@NonNull Uri sourceUri) {
+            private Builder(@TranscodingType int type, @NonNull Uri sourceUri,
+                    @NonNull Uri destinationUri) {
+                mType = type;
+
                 if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) {
                     throw new IllegalArgumentException(
                             "You must specify a non-empty source Uri.");
                 }
                 mSourceUri = sourceUri;
-                return this;
+
+                if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
+                    throw new IllegalArgumentException(
+                            "You must specify a non-empty destination Uri.");
+                }
+                mDestinationUri = destinationUri;
             }
 
             /**
              * Specifies the fileDescriptor opened from the source media file.
              *
              * This call is optional. If the source fileDescriptor is provided, TranscodingSession
-             * will use it directly instead of opening the uri from {@link #setSourceUri(Uri)}. It
-             * is client's responsibility to make sure the fileDescriptor is opened from the source
-             * uri.
+             * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}.
+             * It is client's responsibility to make sure the fileDescriptor is opened from the
+             * source uri.
              * @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file.
              * @return The same builder instance.
              * @throws IllegalArgumentException if fileDescriptor is invalid.
              */
             @NonNull
-            public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) {
+            public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) {
                 if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
                     throw new IllegalArgumentException(
                             "Invalid source descriptor.");
                 }
                 mSourceFileDescriptor = fileDescriptor;
-                return this;
-            }
-
-            /**
-             * Specifies the uri of the destination media file.
-             *
-             * Client must set the destination Uri. If client also provides the destination
-             * fileDescriptor through {@link #setDestinationFileDescriptor(ParcelFileDescriptor)},
-             * TranscodingSession will use the fd instead of calling back to the client to open the
-             * destinationUri.
-             * @param destinationUri Content uri for the destination media file.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if Uri is null or empty.
-             */
-            @NonNull
-            public Builder setDestinationUri(@NonNull Uri destinationUri) {
-                if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-empty destination Uri.");
-                }
-                mDestinationUri = destinationUri;
-                return this;
+                return self();
             }
 
             /**
              * Specifies the fileDescriptor opened from the destination media file.
              *
              * This call is optional. If the destination fileDescriptor is provided,
-             * TranscodingSession will use it directly instead of opening the uri from
-             * {@link #setDestinationUri(Uri)} upon transcoding starts. It is client's
+             * TranscodingSession will use it directly instead of opening the source uri from
+             * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's
              * responsibility to make sure the fileDescriptor is opened from the destination uri.
              * @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media
              *                       file.
@@ -833,46 +740,54 @@
              * @throws IllegalArgumentException if fileDescriptor is invalid.
              */
             @NonNull
-            public Builder setDestinationFileDescriptor(
+            public T setDestinationFileDescriptor(
                     @NonNull ParcelFileDescriptor fileDescriptor) {
                 if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
                     throw new IllegalArgumentException(
                             "Invalid destination descriptor.");
                 }
                 mDestinationFileDescriptor = fileDescriptor;
-                return this;
+                return self();
             }
 
             /**
              * Specify the UID of the client that this request is for.
+             * <p>
+             * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
+             * pid. Note that the permission check happens on the service side upon starting the
+             * transcoding. If the client does not have the permission, the transcoding will fail.
+             *
              * @param uid client Uid.
              * @return The same builder instance.
              * @throws IllegalArgumentException if uid is invalid.
-             * TODO(hkuang): Check the permission if it is allowed.
              */
             @NonNull
-            public Builder setClientUid(int uid) {
+            public T setClientUid(int uid) {
                 if (uid < 0) {
                     throw new IllegalArgumentException("Invalid Uid");
                 }
                 mClientUid = uid;
-                return this;
+                return self();
             }
 
             /**
-             * Specify the PID of the client that this request is for.
+             * Specify the pid of the client that this request is for.
+             * <p>
+             * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
+             * pid. Note that the permission check happens on the service side upon starting the
+             * transcoding. If the client does not have the permission, the transcoding will fail.
+             *
              * @param pid client Pid.
              * @return The same builder instance.
              * @throws IllegalArgumentException if pid is invalid.
-             * TODO(hkuang): Check the permission if it is allowed.
              */
             @NonNull
-            public Builder setClientPid(int pid) {
+            public T setClientPid(int pid) {
                 if (pid < 0) {
                     throw new IllegalArgumentException("Invalid pid");
                 }
                 mClientPid = pid;
-                return this;
+                return self();
             }
 
             /**
@@ -881,64 +796,15 @@
              * @param priority Must be one of the {@code PRIORITY_*}
              * @return The same builder instance.
              * @throws IllegalArgumentException if flags is invalid.
+             * @hide
              */
             @NonNull
-            public Builder setPriority(@TranscodingPriority int priority) {
+            public T setPriority(@TranscodingPriority int priority) {
                 if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) {
                     throw new IllegalArgumentException("Invalid priority: " + priority);
                 }
                 mPriority = priority;
-                return this;
-            }
-
-            /**
-             * Specifies the type of transcoding.
-             * <p> Clients must provide the source and destination that corresponds to the
-             * transcoding type.
-             *
-             * @param type Must be one of the {@code TRANSCODING_TYPE_*}
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if flags is invalid.
-             */
-            @NonNull
-            public Builder setType(@TranscodingType int type) {
-                if (type != TRANSCODING_TYPE_VIDEO && type != TRANSCODING_TYPE_IMAGE) {
-                    throw new IllegalArgumentException("Invalid transcoding type");
-                }
-                mType = type;
-                return this;
-            }
-
-            /**
-             * Specifies the desired video track format in the destination media file.
-             * <p>Client could only specify the settings that matters to them, e.g. codec format or
-             * bitrate. And by default, transcoding will preserve the original video's
-             * settings(bitrate, framerate, resolution) if not provided.
-             * <p>Note that some settings may silently fail to apply if the device does not
-             * support them.
-             * TODO(hkuang): Add MediaTranscodeUtil to help client generate transcoding setting.
-             * TODO(hkuang): Add MediaTranscodeUtil to check if the setting is valid.
-             *
-             * @param videoFormat MediaFormat containing the settings that client wants override in
-             *                    the original video's video track.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if videoFormat is invalid.
-             */
-            @NonNull
-            public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
-                if (videoFormat == null) {
-                    throw new IllegalArgumentException("videoFormat must not be null");
-                }
-
-                // Check if the MediaFormat is for video by looking at the MIME type.
-                String mime = videoFormat.containsKey(MediaFormat.KEY_MIME)
-                        ? videoFormat.getString(MediaFormat.KEY_MIME) : null;
-                if (mime == null || !mime.startsWith("video/")) {
-                    throw new IllegalArgumentException("Invalid video format: wrong mime type");
-                }
-
-                mVideoTrackFormat = videoFormat;
-                return this;
+                return self();
             }
 
             /**
@@ -949,44 +815,9 @@
              */
             @VisibleForTesting
             @NonNull
-            public Builder setTestConfig(@NonNull TranscodingTestConfig config) {
+            public T setTestConfig(@NonNull TranscodingTestConfig config) {
                 mTestConfig = config;
-                return this;
-            }
-
-            /**
-             * @return a new {@link TranscodingRequest} instance successfully initialized with all
-             *     the parameters set on this <code>Builder</code>.
-             * @throws UnsupportedOperationException if the parameters set on the
-             *         <code>Builder</code> were incompatible, or if they are not supported by the
-             *         device.
-             */
-            @NonNull
-            public TranscodingRequest build() {
-                if (mSourceUri == null) {
-                    throw new UnsupportedOperationException("Source URI must not be null");
-                }
-
-                if (mDestinationUri == null) {
-                    throw new UnsupportedOperationException("Destination URI must not be null");
-                }
-
-                if (mPriority == PRIORITY_UNKNOWN) {
-                    throw new UnsupportedOperationException("Must specify transcoding priority");
-                }
-
-                // Only support video transcoding now.
-                if (mType != TRANSCODING_TYPE_VIDEO) {
-                    throw new UnsupportedOperationException("Only supports video transcoding now");
-                }
-
-                // Must provide video track format for video transcoding.
-                if (mType == TRANSCODING_TYPE_VIDEO && mVideoTrackFormat == null) {
-                    throw new UnsupportedOperationException(
-                            "Must provide video track format for video transcoding");
-                }
-
-                return new TranscodingRequest(this);
+                return self();
             }
         }
 
@@ -1201,6 +1032,206 @@
     }
 
     /**
+     * VideoTranscodingRequest encapsulates the configuration for transcoding a video.
+     */
+    public static final class VideoTranscodingRequest extends TranscodingRequest {
+        /**
+         * Desired output video format of the destination file.
+         * <p> If this is null, source file's video track will be passed through and copied to the
+         * destination file.
+         */
+        private @Nullable MediaFormat mVideoTrackFormat = null;
+
+        /**
+         * Desired output audio format of the destination file.
+         * <p> If this is null, source file's audio track will be passed through and copied to the
+         * destination file.
+         */
+        private @Nullable MediaFormat mAudioTrackFormat = null;
+
+        private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) {
+            super(builder);
+            mVideoTrackFormat = builder.mVideoTrackFormat;
+            mAudioTrackFormat = builder.mAudioTrackFormat;
+        }
+
+        /**
+         * Return the video track format of the transcoding.
+         * This will be null if client has not specified the video track format.
+         */
+        @NonNull
+        public MediaFormat getVideoTrackFormat() {
+            return mVideoTrackFormat;
+        }
+
+        @Override
+        void writeFormatToParcel(TranscodingRequestParcel parcel) {
+            parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
+        }
+
+        /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
+        private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
+            if (format == null) {
+                throw new IllegalArgumentException("Invalid MediaFormat");
+            }
+
+            TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
+
+            if (format.containsKey(MediaFormat.KEY_MIME)) {
+                String mime = format.getString(MediaFormat.KEY_MIME);
+                if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
+                    trackFormat.codecType = TranscodingVideoCodecType.kAvc;
+                } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
+                    trackFormat.codecType = TranscodingVideoCodecType.kHevc;
+                } else {
+                    throw new UnsupportedOperationException("Only support transcode to avc/hevc");
+                }
+            }
+
+            if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
+                int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
+                if (bitrateBps <= 0) {
+                    throw new IllegalArgumentException("Bitrate must be larger than 0");
+                }
+                trackFormat.bitrateBps = bitrateBps;
+            }
+
+            if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
+                    MediaFormat.KEY_HEIGHT)) {
+                int width = format.getInteger(MediaFormat.KEY_WIDTH);
+                int height = format.getInteger(MediaFormat.KEY_HEIGHT);
+                if (width <= 0 || height <= 0) {
+                    throw new IllegalArgumentException("Width and height must be larger than 0");
+                }
+                // TODO: Validate the aspect ratio after adding scaling.
+                trackFormat.width = width;
+                trackFormat.height = height;
+            }
+
+            if (format.containsKey(MediaFormat.KEY_PROFILE)) {
+                int profile = format.getInteger(MediaFormat.KEY_PROFILE);
+                if (profile <= 0) {
+                    throw new IllegalArgumentException("Invalid codec profile");
+                }
+                // TODO: Validate the profile according to codec type.
+                trackFormat.profile = profile;
+            }
+
+            if (format.containsKey(MediaFormat.KEY_LEVEL)) {
+                int level = format.getInteger(MediaFormat.KEY_LEVEL);
+                if (level <= 0) {
+                    throw new IllegalArgumentException("Invalid codec level");
+                }
+                // TODO: Validate the level according to codec type.
+                trackFormat.level = level;
+            }
+
+            return trackFormat;
+        }
+
+        /**
+         * Builder class for {@link VideoTranscodingRequest}.
+         */
+        public static final class Builder extends
+                TranscodingRequest.Builder<VideoTranscodingRequest.Builder> {
+            /**
+             * Desired output video format of the destination file.
+             * <p> If this is null, source file's video track will be passed through and
+             * copied to the destination file.
+             */
+            private @Nullable MediaFormat mVideoTrackFormat = null;
+
+            /**
+             * Desired output audio format of the destination file.
+             * <p> If this is null, source file's audio track will be passed through and copied
+             * to the destination file.
+             */
+            private @Nullable MediaFormat mAudioTrackFormat = null;
+
+            /**
+             * Creates a builder for building {@link VideoTranscodingRequest}s.
+             *
+             * <p> Client could only specify the settings that matters to them, e.g. codec format or
+             * bitrate. And by default, transcoding will preserve the original video's settings
+             * (bitrate, framerate, resolution) if not provided.
+             * <p>Note that some settings may silently fail to apply if the device does not support
+             * them.
+             * @param sourceUri Content uri for the source media file.
+             * @param destinationUri Content uri for the destination media file.
+             * @param videoFormat MediaFormat containing the settings that client wants override in
+             *                    the original video's video track.
+             * @throws IllegalArgumentException if videoFormat is invalid.
+             */
+            public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri,
+                    @NonNull MediaFormat videoFormat) {
+                super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri);
+                setVideoTrackFormat(videoFormat);
+            }
+
+            @Override
+            @NonNull
+            public Builder setClientUid(int uid) {
+                super.setClientUid(uid);
+                return self();
+            }
+
+            @Override
+            @NonNull
+            public Builder setClientPid(int pid) {
+                super.setClientPid(pid);
+                return self();
+            }
+
+            @Override
+            @NonNull
+            public Builder setSourceFileDescriptor(ParcelFileDescriptor fd) {
+                super.setSourceFileDescriptor(fd);
+                return self();
+            }
+
+            @Override
+            @NonNull
+            public Builder setDestinationFileDescriptor(ParcelFileDescriptor fd) {
+                super.setDestinationFileDescriptor(fd);
+                return self();
+            }
+
+            private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
+                if (videoFormat == null) {
+                    throw new IllegalArgumentException("videoFormat must not be null");
+                }
+
+                // Check if the MediaFormat is for video by looking at the MIME type.
+                String mime = videoFormat.containsKey(MediaFormat.KEY_MIME)
+                        ? videoFormat.getString(MediaFormat.KEY_MIME) : null;
+                if (mime == null || !mime.startsWith("video/")) {
+                    throw new IllegalArgumentException("Invalid video format: wrong mime type");
+                }
+
+                mVideoTrackFormat = videoFormat;
+            }
+
+            /**
+             * @return a new {@link TranscodingRequest} instance successfully initialized
+             * with all the parameters set on this <code>Builder</code>.
+             * @throws UnsupportedOperationException if the parameters set on the
+             *                                       <code>Builder</code> were incompatible, or
+             *                                       if they are not supported by the
+             *                                       device.
+             */
+            @NonNull
+            public VideoTranscodingRequest build() {
+                return new VideoTranscodingRequest(this);
+            }
+
+            @Override
+            VideoTranscodingRequest.Builder self() {
+                return this;
+            }
+        }
+    }
+
+    /**
      * Handle to an enqueued transcoding operation. An instance of this class represents a single
      * enqueued transcoding operation. The caller can use that instance to query the status or
      * progress, and to get the result once the operation has completed.
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 4e5b3ba..0eff83c 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -22,13 +22,9 @@
 
     multilib: {
         lib32: {
-            // TODO(b/142944043): Remove version script when libsigchain is a DSO.
-            version_script: "version-script32.txt",
             suffix: "32",
         },
         lib64: {
-            // TODO(b/142944043): Remove version script when libsigchain is a DSO.
-            version_script: "version-script64.txt",
             suffix: "64",
         },
     },
@@ -43,6 +39,13 @@
         "libhidlbase",
         "liblog",
         "libnativeloader",
+
+        // Even though app_process doesn't call into libsigchain, we need to
+        // make sure it's in the DT list of app_process, as we want all code
+        // in app_process and the libraries it loads to find libsigchain
+        // symbols before libc symbols.
+        "libsigchain",
+
         "libutils",
 
         // This is a list of libraries that need to be included in order to avoid
@@ -52,8 +55,6 @@
         "libwilhelm",
     ],
 
-    whole_static_libs: ["libsigchain"],
-
     compile_multilib: "both",
 
     cflags: [
diff --git a/cmds/app_process/version-script32.txt b/cmds/app_process/version-script32.txt
deleted file mode 100644
index 70810e0..0000000
--- a/cmds/app_process/version-script32.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-global:
-  EnsureFrontOfChain;
-  AddSpecialSignalHandlerFn;
-  RemoveSpecialSignalHandlerFn;
-  SkipAddSignalHandler;
-  bsd_signal;
-  sigaction;
-  sigaction64;
-  signal;
-  sigprocmask;
-  sigprocmask64;
-local:
-  *;
-};
diff --git a/cmds/app_process/version-script64.txt b/cmds/app_process/version-script64.txt
deleted file mode 100644
index 7bcd76b..0000000
--- a/cmds/app_process/version-script64.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-global:
-  EnsureFrontOfChain;
-  AddSpecialSignalHandlerFn;
-  RemoveSpecialSignalHandlerFn;
-  SkipAddSignalHandler;
-  sigaction;
-  sigaction64;
-  signal;
-  sigprocmask;
-  sigprocmask64;
-local:
-  *;
-};
diff --git a/core/api/current.txt b/core/api/current.txt
index 2b5196b..18c049f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8257,13 +8257,13 @@
   }
 
   public class NetworkStatsManager {
-    method public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
-    method public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
-    method public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
-    method public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
-    method public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
-    method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
-    method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
     method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback);
     method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
     method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback);
@@ -22318,6 +22318,8 @@
     field public static final String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
     field public static final String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
     field public static final String MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+    field public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
+    field public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
     field public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
     field public static final String MIMETYPE_AUDIO_OPUS = "audio/opus";
     field public static final String MIMETYPE_AUDIO_QCELP = "audio/qcelp";
@@ -40528,6 +40530,7 @@
     field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
     field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
     field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
+    field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
     field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
     field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -40573,6 +40576,7 @@
     field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+    field public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL = "vt_upgrade_supported_for_downgraded_rtt_call";
     field public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
     field public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
     field public static final String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
@@ -42181,6 +42185,10 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
   }
 
+  public static interface TelephonyCallback.PhysicalChannelConfigListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+  }
+
   public static interface TelephonyCallback.PreciseDataConnectionStateListener {
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
   }
@@ -52403,6 +52411,17 @@
     method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
   }
 
+  public final class UiTranslationManager {
+    method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback);
+    method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback);
+  }
+
+  public interface UiTranslationStateCallback {
+    method public void onFinished();
+    method public void onPaused();
+    method public void onStarted(@NonNull String, @NonNull String);
+  }
+
   public final class ViewTranslationRequest implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.view.autofill.AutofillId getAutofillId();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index a140e8a..51b6967 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -258,6 +258,10 @@
     method public default int getStability();
   }
 
+  public class Process {
+    field public static final int VPN_UID = 1016; // 0x3f8
+  }
+
   public class StatsServiceManager {
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 54c6663..2a99aaa 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2837,21 +2837,29 @@
     method @NonNull public String getPackageName();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
+    field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
+    field public static final int STATE_MODIFIABLE_UNVERIFIED = 3; // 0x3
+    field public static final int STATE_MODIFIABLE_VERIFIED = 4; // 0x4
+    field public static final int STATE_NO_RESPONSE = 0; // 0x0
+    field public static final int STATE_SUCCESS = 1; // 0x1
+    field public static final int STATE_UNMODIFIABLE = 2; // 0x2
   }
 
   public final class DomainVerificationManager {
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
-    method public static boolean isStateModifiable(int);
-    method public static boolean isStateVerified(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public int setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public int setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+    field public static final int ERROR_DOMAIN_SET_ID_INVALID = 1; // 0x1
+    field public static final int ERROR_DOMAIN_SET_ID_NULL = 2; // 0x2
+    field public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3; // 0x3
+    field public static final int ERROR_INVALID_STATE_CODE = 6; // 0x6
+    field public static final int ERROR_UNABLE_TO_APPROVE = 5; // 0x5
+    field public static final int ERROR_UNKNOWN_DOMAIN = 4; // 0x4
     field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
-    field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
-    field public static final int STATE_NO_RESPONSE = 0; // 0x0
-    field public static final int STATE_SUCCESS = 1; // 0x1
+    field public static final int STATUS_OK = 0; // 0x0
   }
 
   public final class DomainVerificationRequest implements android.os.Parcelable {
@@ -10452,11 +10460,32 @@
     method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport);
     method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState);
-    method @NonNull public abstract android.telecom.DiagnosticCall onInitializeDiagnosticCall(@NonNull android.telecom.Call.Details);
-    method public abstract void onRemoveDiagnosticCall(@NonNull android.telecom.DiagnosticCall);
+    method @NonNull public abstract android.telecom.CallDiagnostics onInitializeCallDiagnostics(@NonNull android.telecom.Call.Details);
+    method public abstract void onRemoveCallDiagnostics(@NonNull android.telecom.CallDiagnostics);
     field public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService";
   }
 
+  public abstract class CallDiagnostics {
+    ctor public CallDiagnostics();
+    method public final void clearDiagnosticMessage(int);
+    method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
+    method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
+    method @Nullable public abstract CharSequence onCallDisconnected(int, int);
+    method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
+    method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
+    method public abstract void onReceiveDeviceToDeviceMessage(int, int);
+    method public final void sendDeviceToDeviceMessage(int, int);
+    field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
+    field public static final int BATTERY_STATE_GOOD = 2; // 0x2
+    field public static final int BATTERY_STATE_LOW = 1; // 0x1
+    field public static final int COVERAGE_GOOD = 2; // 0x2
+    field public static final int COVERAGE_POOR = 1; // 0x1
+    field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2
+    field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
+    field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
+    field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
+  }
+
   public static class CallScreeningService.CallResponse.Builder {
     method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean);
   }
@@ -10517,25 +10546,8 @@
     method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
   }
 
-  public abstract class DiagnosticCall {
-    ctor public DiagnosticCall();
-    method public final void clearDiagnosticMessage(int);
-    method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
-    method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
-    method @Nullable public abstract CharSequence onCallDisconnected(int, int);
-    method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
-    method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
-    method public abstract void onReceiveDeviceToDeviceMessage(int, int);
-    method public final void sendDeviceToDeviceMessage(int, int);
-    field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
-    field public static final int BATTERY_STATE_GOOD = 2; // 0x2
-    field public static final int BATTERY_STATE_LOW = 1; // 0x1
-    field public static final int COVERAGE_GOOD = 2; // 0x2
-    field public static final int COVERAGE_POOR = 1; // 0x1
-    field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2
-    field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
-    field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
-    field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
+  @Deprecated public abstract class DiagnosticCall extends android.telecom.CallDiagnostics {
+    ctor @Deprecated public DiagnosticCall();
   }
 
   public abstract class InCallService extends android.app.Service {
@@ -11605,10 +11617,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
   }
 
-  public static interface TelephonyCallback.PhysicalChannelConfigListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
-  }
-
   public static interface TelephonyCallback.PreciseCallStateListener {
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
   }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1c487e5..27b19bc 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -57,7 +57,6 @@
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LongSparseArray;
@@ -8114,8 +8113,8 @@
                 } else if (collectionMode == COLLECT_SYNC
                         // Only collect app-ops when the proxy is trusted
                         && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
-                        myUid) == PackageManager.PERMISSION_GRANTED || isTrustedVoiceServiceProxy(
-                        mContext, mContext.getOpPackageName(), op, mContext.getUserId()))) {
+                        myUid) == PackageManager.PERMISSION_GRANTED ||
+                        Binder.getCallingUid() == proxiedUid)) {
                     collectNotedOpSync(op, proxiedAttributionTag);
                 }
             }
@@ -8126,28 +8125,6 @@
         }
     }
 
-    /**
-     * Checks if the voice recognition service is a trust proxy.
-     *
-     * @return {@code true} if the package is a trust voice recognition service proxy
-     * @hide
-     */
-    public static boolean isTrustedVoiceServiceProxy(Context context, String packageName,
-            int code, int userId) {
-        // This is a workaround for R QPR, new API change is not allowed. We only allow the current
-        // voice recognizer is also the voice interactor to noteproxy op.
-        if (code != OP_RECORD_AUDIO) {
-            return false;
-        }
-        final String voiceRecognitionComponent = Settings.Secure.getStringForUser(
-                context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE, userId);
-
-        final String voiceRecognitionServicePackageName =
-                getComponentPackageNameFromString(voiceRecognitionComponent);
-        return (Objects.equals(packageName, voiceRecognitionServicePackageName))
-                && isPackagePreInstalled(context, packageName, userId);
-    }
-
     private static String getComponentPackageNameFromString(String from) {
         ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null;
         return componentName != null ? componentName.getPackageName() : "";
@@ -8522,8 +8499,7 @@
                         // Only collect app-ops when the proxy is trusted
                         && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
                         Process.myUid()) == PackageManager.PERMISSION_GRANTED
-                        || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), opInt,
-                        mContext.getUserId()))) {
+                        || Binder.getCallingUid() == proxiedUid)) {
                     collectNotedOpSync(opInt, proxiedAttributionTag);
                 }
             }
@@ -9172,7 +9148,7 @@
                 try {
                     sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                             FULL_LOG, false);
-                } catch (SecurityException e) {
+                } catch (Exception e) {
                     // This should not happen, but it may, in rare cases
                     sFullLog = false;
                 }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index afcf63b..1765849 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -331,10 +331,12 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Send {@link "
-            + "android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} instead.")
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "This operation"
+            + " is not allowed anymore, please see {@link android.content"
+            + ".Intent#ACTION_CLOSE_SYSTEM_DIALOGS} for more details.")
     @TestApi
     public void collapsePanels() {
+
         try {
             final IStatusBarService svc = getService();
             if (svc != null) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 930717b..56aa9a2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2987,7 +2987,7 @@
     /**
      * Checks if it's safe to run operations that can be affected by the given {@code reason}.
      *
-     * <p><b>Note:/b> notice that the operation safety state might change between the time this
+     * <p><b>Note:</b> notice that the operation safety state might change between the time this
      * method returns and the operation's method is called, so calls to the latter could still throw
      * a {@link UnsafeStateException} even when this method returns {@code true}.
      *
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 098d8b6..9f1132b 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.WorkerThread;
 import android.app.usage.NetworkStats.Bucket;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -201,6 +202,7 @@
      * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
      * metered {@link NetworkStats.Bucket#METERED_ALL},
      * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -219,6 +221,7 @@
      * @return Bucket object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public Bucket querySummaryForDevice(int networkType, String subscriberId,
             long startTime, long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
@@ -240,6 +243,7 @@
      * uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
      * metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
      * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,6 +262,7 @@
      * @return Bucket object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
@@ -283,6 +288,7 @@
      * means buckets' start and end timestamps are going to be the same as the 'startTime' and
      * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
      * be the same.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -301,6 +307,7 @@
      * @return Statistics object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
@@ -326,9 +333,11 @@
 
     /**
      * Query network usage statistics details for a given uid.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
+    @WorkerThread
     public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
             long startTime, long endTime, int uid) throws SecurityException {
         return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -344,9 +353,11 @@
 
     /**
      * Query network usage statistics details for a given uid and tag.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
+    @WorkerThread
     public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
             long startTime, long endTime, int uid, int tag) throws SecurityException {
         return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -365,6 +376,7 @@
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -387,6 +399,7 @@
      * @return Statistics object or null if an error happened during statistics collection.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
+    @WorkerThread
     public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
             long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
         NetworkTemplate template;
@@ -425,6 +438,7 @@
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -443,6 +457,7 @@
      * @return Statistics object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8fd0de7..a6b4b47 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -520,9 +520,7 @@
                 return;
             }
             int layoutId = rvToApply.getLayoutId();
-            // If our stale view has been prepared to match active, and the new
-            // layout matches, try recycling it
-            if (content == null && layoutId == mLayoutId) {
+            if (rvToApply.canRecycleView(mView)) {
                 try {
                     rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
                             mColorResources);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f8dd0e1..190ef1a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3876,7 +3876,7 @@
      * @see #getSystemService(String)
      * @hide
      */
-    public static final String POWER_STATS_SERVICE = "power_stats";
+    public static final String POWER_STATS_SERVICE = "powerstats";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index fe8e4d7..6f07dd7 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -183,6 +184,17 @@
     @SystemApi
     public boolean handleAllWebDataURI;
 
+    /**
+     * Whether the resolved {@link IntentFilter} declares {@link Intent#CATEGORY_BROWSABLE} and is
+     * thus allowed to automatically resolve an {@link Intent} as it's assumed the action is safe
+     * for the user.
+     *
+     * Note that the above doesn't apply when this is the only result is returned in the candidate
+     * set, as the system will not prompt before opening the result. It only applies when there are
+     * multiple candidates.
+     */
+    private final boolean mAutoResolutionAllowed;
+
     /** {@hide} */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public ComponentInfo getComponentInfo() {
@@ -364,8 +376,26 @@
                 && INTENT_FORWARDER_ACTIVITY.equals(activityInfo.targetActivity);
     }
 
+    /**
+     * @see #mAutoResolutionAllowed
+     * @hide
+     */
+    public boolean isAutoResolutionAllowed() {
+        return mAutoResolutionAllowed;
+    }
+
     public ResolveInfo() {
         targetUserId = UserHandle.USER_CURRENT;
+
+        // It's safer to assume that an unaware caller that constructs a ResolveInfo doesn't
+        // accidentally mark a result as auto resolveable.
+        mAutoResolutionAllowed = false;
+    }
+
+    /** @hide */
+    public ResolveInfo(boolean autoResolutionAllowed) {
+        targetUserId = UserHandle.USER_CURRENT;
+        mAutoResolutionAllowed = autoResolutionAllowed;
     }
 
     public ResolveInfo(ResolveInfo orig) {
@@ -386,6 +416,7 @@
         system = orig.system;
         targetUserId = orig.targetUserId;
         handleAllWebDataURI = orig.handleAllWebDataURI;
+        mAutoResolutionAllowed = orig.mAutoResolutionAllowed;
         isInstantAppAvailable = orig.isInstantAppAvailable;
     }
 
@@ -450,6 +481,7 @@
         dest.writeInt(noResourceId ? 1 : 0);
         dest.writeInt(iconResourceId);
         dest.writeInt(handleAllWebDataURI ? 1 : 0);
+        dest.writeInt(mAutoResolutionAllowed ? 1 : 0);
         dest.writeInt(isInstantAppAvailable ? 1 : 0);
     }
 
@@ -498,6 +530,7 @@
         noResourceId = source.readInt() != 0;
         iconResourceId = source.readInt();
         handleAllWebDataURI = source.readInt() != 0;
+        mAutoResolutionAllowed = source.readInt() != 0;
         isInstantAppAvailable = source.readInt() != 0;
     }
 
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
index 7c335b1..62277ef 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -43,9 +43,50 @@
  */
 @SystemApi
 @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
-        genEqualsHashCode = true)
+        genEqualsHashCode = true, genHiddenConstDefs = true)
 public final class DomainVerificationInfo implements Parcelable {
 
+    // Implementation note: the following states are OUTPUT only. Any value that is synonymous with
+    // a value in DomainVerificationState must be the EXACT same integer, so that state
+    // transformation does not have to occur when sending input into the system, assuming that the
+    // system only accepts those synonymous values. The public API values declared here are only
+    // used when exiting the system server to prepare this data object for consumption by the
+    // verification agent. These constants should only be referenced inside public API classes.
+    // The server must use DomainVerificationState.
+
+    /**
+     * No response has been recorded by either the system or any verification agent.
+     */
+    public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+
+    /**
+     * The domain has been explicitly verified.
+     */
+    public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+
+    /**
+     * Indicates the host cannot be modified by the verification agent.
+     */
+    public static final int STATE_UNMODIFIABLE = 2;
+
+    /**
+     * Indicates the host can be modified by the verification agent and is not considered verified.
+     */
+    public static final int STATE_MODIFIABLE_UNVERIFIED = 3;
+
+    /**
+     * Indicates the host can be modified by the verification agent and is considered verified.
+     */
+    public static final int STATE_MODIFIABLE_VERIFIED = 4;
+
+    /**
+     * The first available custom response code. This and any greater integer, along with {@link
+     * #STATE_SUCCESS} are the only values settable by the verification agent. All custom values
+     * will be treated as if the domain is unverified.
+     */
+    public static final int STATE_FIRST_VERIFIER_DEFINED =
+            DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+
     /**
      * A domain verification ID for use in later API calls. This represents the snapshot of the
      * domains for a package on device, and will be invalidated whenever the package changes.
@@ -74,16 +115,12 @@
 
     /**
      * Map of host names to their current state. State is an integer, which defaults to {@link
-     * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
-     * verification agent (the intended consumer of this API), which can be equal to {@link
-     * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
-     * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+     * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+     * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+     * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
      * <p>
-     * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
-     * verification agent may be able to act on these reserved values, and this ability can be
-     * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
-     * the agent attempt to verify all domains that it can modify the state of, even if it does not
-     * understand the meaning of those values.
+     * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+     * that the agent attempt to verify all domains that it can modify the state of.
      */
     @NonNull
     private final Map<String, Integer> mHostToStateMap;
@@ -112,6 +149,39 @@
     //@formatter:off
 
 
+    /** @hide */
+    @android.annotation.IntDef(prefix = "STATE_", value = {
+        STATE_NO_RESPONSE,
+        STATE_SUCCESS,
+        STATE_UNMODIFIABLE,
+        STATE_MODIFIABLE_UNVERIFIED,
+        STATE_MODIFIABLE_VERIFIED,
+        STATE_FIRST_VERIFIER_DEFINED
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface State {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String stateToString(@State int value) {
+        switch (value) {
+            case STATE_NO_RESPONSE:
+                    return "STATE_NO_RESPONSE";
+            case STATE_SUCCESS:
+                    return "STATE_SUCCESS";
+            case STATE_UNMODIFIABLE:
+                    return "STATE_UNMODIFIABLE";
+            case STATE_MODIFIABLE_UNVERIFIED:
+                    return "STATE_MODIFIABLE_UNVERIFIED";
+            case STATE_MODIFIABLE_VERIFIED:
+                    return "STATE_MODIFIABLE_VERIFIED";
+            case STATE_FIRST_VERIFIER_DEFINED:
+                    return "STATE_FIRST_VERIFIER_DEFINED";
+            default: return Integer.toHexString(value);
+        }
+    }
+
     /**
      * Creates a new DomainVerificationInfo.
      *
@@ -134,16 +204,12 @@
      *   The package name that this data corresponds to.
      * @param hostToStateMap
      *   Map of host names to their current state. State is an integer, which defaults to {@link
-     *   DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
-     *   verification agent (the intended consumer of this API), which can be equal to {@link
-     *   DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
-     *   DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+     *   #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+     *   consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+     *   to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
      *   <p>
-     *   Any value non-inclusive between those 2 values are reserved for use by the system. The domain
-     *   verification agent may be able to act on these reserved values, and this ability can be
-     *   queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
-     *   the agent attempt to verify all domains that it can modify the state of, even if it does not
-     *   understand the meaning of those values.
+     *   Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+     *   that the agent attempt to verify all domains that it can modify the state of.
      * @hide
      */
     @DataClass.Generated.Member
@@ -195,16 +261,12 @@
 
     /**
      * Map of host names to their current state. State is an integer, which defaults to {@link
-     * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
-     * verification agent (the intended consumer of this API), which can be equal to {@link
-     * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
-     * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
+     * #STATE_NO_RESPONSE}. State can be modified by the domain verification agent (the intended
+     * consumer of this API), which can be equal to {@link #STATE_SUCCESS} when verified, or equal
+     * to or greater than {@link #STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
      * <p>
-     * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
-     * verification agent may be able to act on these reserved values, and this ability can be
-     * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
-     * the agent attempt to verify all domains that it can modify the state of, even if it does not
-     * understand the meaning of those values.
+     * Hosts which cannot be edited will be assigned {@link #STATE_UNMODIFIABLE}. It is expected
+     * that the agent attempt to verify all domains that it can modify the state of.
      */
     @DataClass.Generated.Member
     public @NonNull Map<String,Integer> getHostToStateMap() {
@@ -320,10 +382,10 @@
     };
 
     @DataClass.Generated(
-            time = 1614721812023L,
+            time = 1615317187669L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
-            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate  java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+            inputSignatures = "public static final  int STATE_NO_RESPONSE\npublic static final  int STATE_SUCCESS\npublic static final  int STATE_UNMODIFIABLE\npublic static final  int STATE_MODIFIABLE_UNVERIFIED\npublic static final  int STATE_MODIFIABLE_VERIFIED\npublic static final  int STATE_FIRST_VERIFIER_DEFINED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate  java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index f7c81bcf..55e19f2 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -60,154 +60,97 @@
             "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
 
     /**
-     * No response has been recorded by either the system or any verification agent.
+     * Default return code for when a method has succeeded.
      *
      * @hide
      */
     @SystemApi
-    public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+    public static final int STATUS_OK = 0;
 
     /**
-     * The verification agent has explicitly verified the domain at some point.
+     * The provided domain set ID was invalid, probably due to the package being updated between
+     * the initial request that provided the ID and the method call that used it. This usually
+     * means the work being processed by the verification agent is outdated and a new request
+     * should be scheduled, which should already be in progress as part of the
+     * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
      *
      * @hide
      */
     @SystemApi
-    public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+    public static final int ERROR_DOMAIN_SET_ID_INVALID = 1;
 
     /**
-     * The first available custom response code. This and any greater integer, along with {@link
-     * #STATE_SUCCESS} are the only values settable by the verification agent. All values will be
-     * treated as if the domain is unverified.
+     * The provided domain set ID was null. This is a developer error.
      *
      * @hide
      */
     @SystemApi
-    public static final int STATE_FIRST_VERIFIER_DEFINED =
-            DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+    public static final int ERROR_DOMAIN_SET_ID_NULL = 2;
 
     /**
-     * @hide
-     */
-    @NonNull
-    public static String stateToDebugString(@DomainVerificationState.State int state) {
-        switch (state) {
-            case DomainVerificationState.STATE_NO_RESPONSE:
-                return "none";
-            case DomainVerificationState.STATE_SUCCESS:
-                return "verified";
-            case DomainVerificationState.STATE_APPROVED:
-                return "approved";
-            case DomainVerificationState.STATE_DENIED:
-                return "denied";
-            case DomainVerificationState.STATE_MIGRATED:
-                return "migrated";
-            case DomainVerificationState.STATE_RESTORED:
-                return "restored";
-            case DomainVerificationState.STATE_LEGACY_FAILURE:
-                return "legacy_failure";
-            case DomainVerificationState.STATE_SYS_CONFIG:
-                return "system_configured";
-            default:
-                return String.valueOf(state);
-        }
-    }
-
-    /**
-     * Checks if a state considers the corresponding domain to be successfully verified. The domain
-     * verification agent may use this to determine whether or not to re-verify a domain.
+     * The provided set of domains was null or empty. This is a developer error.
      *
      * @hide
      */
     @SystemApi
-    public static boolean isStateVerified(@DomainVerificationState.State int state) {
-        switch (state) {
-            case DomainVerificationState.STATE_SUCCESS:
-            case DomainVerificationState.STATE_APPROVED:
-            case DomainVerificationState.STATE_MIGRATED:
-            case DomainVerificationState.STATE_RESTORED:
-            case DomainVerificationState.STATE_SYS_CONFIG:
-                return true;
-            case DomainVerificationState.STATE_NO_RESPONSE:
-            case DomainVerificationState.STATE_DENIED:
-            case DomainVerificationState.STATE_LEGACY_FAILURE:
-            default:
-                return false;
-        }
-    }
+    public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3;
 
     /**
-     * Checks if a state is modifiable by the domain verification agent. This is useful as the
-     * platform may add new state codes in newer versions, and older verification agents can use
-     * this method to determine if a state can be changed without having to be aware of what the new
-     * state means.
+     * The provided set of domains contains a domain not declared by the target package. This
+     * usually means the work being processed by the verification agent is outdated and a new
+     * request should be scheduled, which should already be in progress as part of the
+     * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
      *
      * @hide
      */
     @SystemApi
-    public static boolean isStateModifiable(@DomainVerificationState.State int state) {
-        switch (state) {
-            case DomainVerificationState.STATE_NO_RESPONSE:
-            case DomainVerificationState.STATE_SUCCESS:
-            case DomainVerificationState.STATE_MIGRATED:
-            case DomainVerificationState.STATE_RESTORED:
-            case DomainVerificationState.STATE_LEGACY_FAILURE:
-                return true;
-            case DomainVerificationState.STATE_APPROVED:
-            case DomainVerificationState.STATE_DENIED:
-            case DomainVerificationState.STATE_SYS_CONFIG:
-                return false;
-            default:
-                return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
-        }
-    }
+    public static final int ERROR_UNKNOWN_DOMAIN = 4;
 
     /**
-     * For determine re-verify policy. This is hidden from the domain verification agent so that no
-     * behavior is made based on the result.
+     * The system was unable to select the domain for approval. This indicates another application
+     * has been granted a higher approval, usually through domain verification, and the target
+     * package is unable to override it.
      *
      * @hide
      */
-    public static boolean isStateDefault(@DomainVerificationState.State int state) {
-        switch (state) {
-            case DomainVerificationState.STATE_NO_RESPONSE:
-            case DomainVerificationState.STATE_MIGRATED:
-            case DomainVerificationState.STATE_RESTORED:
-                return true;
-            case DomainVerificationState.STATE_SUCCESS:
-            case DomainVerificationState.STATE_APPROVED:
-            case DomainVerificationState.STATE_DENIED:
-            case DomainVerificationState.STATE_LEGACY_FAILURE:
-            case DomainVerificationState.STATE_SYS_CONFIG:
-            default:
-                return false;
-        }
-    }
+    @SystemApi
+    public static final int ERROR_UNABLE_TO_APPROVE = 5;
 
     /**
+     * The provided state code is incorrect. The domain verification agent is only allowed to
+     * assign {@link DomainVerificationInfo#STATE_SUCCESS} or error codes equal to or greater than
+     * {@link DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED}.
+     *
      * @hide
      */
-    public static final int ERROR_INVALID_DOMAIN_SET = 1;
+    @SystemApi
+    public static final int ERROR_INVALID_STATE_CODE = 6;
+
     /**
+     * Used to communicate through {@link ServiceSpecificException}. Should not be exposed as API.
+     *
      * @hide
      */
-    public static final int ERROR_NAME_NOT_FOUND = 2;
+    public static final int INTERNAL_ERROR_NAME_NOT_FOUND = 1;
 
     /**
      * @hide
      */
     @IntDef(prefix = {"ERROR_"}, value = {
-            ERROR_INVALID_DOMAIN_SET,
-            ERROR_NAME_NOT_FOUND,
+            ERROR_DOMAIN_SET_ID_INVALID,
+            ERROR_DOMAIN_SET_ID_NULL,
+            ERROR_DOMAIN_SET_NULL_OR_EMPTY,
+            ERROR_UNKNOWN_DOMAIN,
+            ERROR_UNABLE_TO_APPROVE,
+            ERROR_INVALID_STATE_CODE
     })
-    private @interface Error {
+    public @interface Error {
     }
 
     private final Context mContext;
 
     private final IDomainVerificationManager mDomainVerificationManager;
 
-
     /**
      * System service to access the domain verification APIs.
      * <p>
@@ -289,27 +232,24 @@
      * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
      * @param domains     List of host names to change the state of.
      * @param state       See {@link DomainVerificationInfo#getHostToStateMap()}.
-     * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
-     *                                  invalid. This usually means the work being processed by the
-     *                                  verification agent is outdated and a new request should be
-     *                                  scheduled, if one has not already been done as part of the
-     *                                  {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
      * @throws NameNotFoundException    If the ID is known to be good, but the package is
      *                                  unavailable. This may be because the package is installed on
      *                                  a volume that is no longer mounted. This error is
      *                                  unrecoverable until the package is available again, and
      *                                  should not be re-tried except on a time scheduled basis.
+     * @return error code or {@link #STATUS_OK} if successful
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
-    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
-            @DomainVerificationState.State int state) throws NameNotFoundException {
+    public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+            int state) throws NameNotFoundException {
         try {
-            mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+            return mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
                     new DomainSet(domains), state);
         } catch (Exception e) {
-            Exception converted = rethrow(e, domainSetId);
+            Exception converted = rethrow(e, null);
             if (converted instanceof NameNotFoundException) {
                 throw (NameNotFoundException) converted;
             } else if (converted instanceof RuntimeException) {
@@ -338,7 +278,7 @@
             mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
                     allowed, mContext.getUserId());
         } catch (Exception e) {
-            Exception converted = rethrow(e, packageName);
+            Exception converted = rethrow(e, null);
             if (converted instanceof NameNotFoundException) {
                 throw (NameNotFoundException) converted;
             } else if (converted instanceof RuntimeException) {
@@ -372,24 +312,24 @@
      * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
      * @param domains     The domains to toggle the state of.
      * @param enabled     Whether or not the app should automatically open the domains specified.
-     * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
-     *                                  invalid.
      * @throws NameNotFoundException    If the ID is known to be good, but the package is
      *                                  unavailable. This may be because the package is installed on
      *                                  a volume that is no longer mounted. This error is
      *                                  unrecoverable until the package is available again, and
      *                                  should not be re-tried except on a time scheduled basis.
+     * @return error code or {@link #STATUS_OK} if successful
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
-    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+    public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
             @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException {
         try {
-            mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
-                    new DomainSet(domains), enabled, mContext.getUserId());
+            return mDomainVerificationManager.setDomainVerificationUserSelection(
+                    domainSetId.toString(), new DomainSet(domains), enabled, mContext.getUserId());
         } catch (Exception e) {
-            Exception converted = rethrow(e, domainSetId);
+            Exception converted = rethrow(e, null);
             if (converted instanceof NameNotFoundException) {
                 throw (NameNotFoundException) converted;
             } else if (converted instanceof RuntimeException) {
@@ -447,123 +387,22 @@
         }
     }
 
-    private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
-        return rethrow(exception, domainSetId, null);
-    }
-
     private Exception rethrow(Exception exception, @Nullable String packageName) {
-        return rethrow(exception, null, packageName);
-    }
-
-    private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
-            @Nullable String packageName) {
         if (exception instanceof ServiceSpecificException) {
-            int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+            int serviceSpecificErrorCode = ((ServiceSpecificException) exception).errorCode;
             if (packageName == null) {
                 packageName = exception.getMessage();
             }
 
-            @Error int managerErrorCode = packedErrorCode & 0xFFFF;
-            switch (managerErrorCode) {
-                case ERROR_INVALID_DOMAIN_SET:
-                    int errorSpecificCode = packedErrorCode >> 16;
-                    return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
-                            domainSetId, packageName, errorSpecificCode));
-                case ERROR_NAME_NOT_FOUND:
-                    return new NameNotFoundException(packageName);
-                default:
-                    return exception;
+            if (serviceSpecificErrorCode == INTERNAL_ERROR_NAME_NOT_FOUND) {
+                return new NameNotFoundException(packageName);
             }
+
+            return exception;
         } else if (exception instanceof RemoteException) {
             return ((RemoteException) exception).rethrowFromSystemServer();
         } else {
             return exception;
         }
     }
-
-    /**
-     * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
-     * provided by the caller is no longer valid. This may be recoverable, and the caller should
-     * re-query the package name associated with the ID using
-     * {@link #getDomainVerificationInfo(String)}
-     * in order to check. If that also fails, then the package is no longer known to the device and
-     * thus all pending work for it should be dropped.
-     *
-     * @hide
-     */
-    public static class InvalidDomainSetException extends IllegalArgumentException {
-
-        public static final int REASON_ID_NULL = 1;
-        public static final int REASON_ID_INVALID = 2;
-        public static final int REASON_SET_NULL_OR_EMPTY = 3;
-        public static final int REASON_UNKNOWN_DOMAIN = 4;
-        public static final int REASON_UNABLE_TO_APPROVE = 5;
-
-        /**
-         * @hide
-         */
-        @IntDef({
-                REASON_ID_NULL,
-                REASON_ID_INVALID,
-                REASON_SET_NULL_OR_EMPTY,
-                REASON_UNKNOWN_DOMAIN,
-                REASON_UNABLE_TO_APPROVE
-        })
-        public @interface Reason {
-        }
-
-        public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName,
-                @Reason int reason) {
-            switch (reason) {
-                case REASON_ID_NULL:
-                    return "Domain set ID cannot be null";
-                case REASON_ID_INVALID:
-                    return "Domain set ID " + domainSetId + " has been invalidated";
-                case REASON_SET_NULL_OR_EMPTY:
-                    return "Domain set cannot be null or empty";
-                case REASON_UNKNOWN_DOMAIN:
-                    return "Domain set contains value that was not declared by the target package "
-                            + packageName;
-                case REASON_UNABLE_TO_APPROVE:
-                    return "Domain set contains value that was owned by another package";
-                default:
-                    return "Unknown failure";
-            }
-        }
-
-        @Reason
-        private final int mReason;
-
-        @Nullable
-        private final UUID mDomainSetId;
-
-        @Nullable
-        private final String mPackageName;
-
-        /**
-         * @hide
-         */
-        public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
-                @Reason int reason) {
-            super(buildMessage(domainSetId, packageName, reason));
-            mDomainSetId = domainSetId;
-            mPackageName = packageName;
-            mReason = reason;
-        }
-
-        @Nullable
-        public UUID getDomainSetId() {
-            return mDomainSetId;
-        }
-
-        @Nullable
-        public String getPackageName() {
-            return mPackageName;
-        }
-
-        @Reason
-        public int getReason() {
-            return mReason;
-        }
-    }
 }
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
index 17593ef..8e28042 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationState.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
@@ -17,15 +17,13 @@
 package android.content.pm.verify.domain;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 
 /**
  * @hide
  */
 public interface DomainVerificationState {
 
-    /**
-     * @hide
-     */
     @IntDef({
             STATE_NO_RESPONSE,
             STATE_SUCCESS,
@@ -42,12 +40,12 @@
 
     // TODO(b/159952358): Document all the places that states need to be updated when one is added
     /**
-     * @see DomainVerificationManager#STATE_NO_RESPONSE
+     * @see DomainVerificationInfo#STATE_NO_RESPONSE
      */
     int STATE_NO_RESPONSE = 0;
 
     /**
-     * @see DomainVerificationManager#STATE_SUCCESS
+     * @see DomainVerificationInfo#STATE_SUCCESS
      */
     int STATE_SUCCESS = 1;
 
@@ -94,7 +92,132 @@
     int STATE_SYS_CONFIG = 7;
 
     /**
-     * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED
+     * @see DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED
      */
     int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000;
+
+    @NonNull
+    static String stateToDebugString(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_NO_RESPONSE:
+                return "none";
+            case DomainVerificationState.STATE_SUCCESS:
+                return "verified";
+            case DomainVerificationState.STATE_APPROVED:
+                return "approved";
+            case DomainVerificationState.STATE_DENIED:
+                return "denied";
+            case DomainVerificationState.STATE_MIGRATED:
+                return "migrated";
+            case DomainVerificationState.STATE_RESTORED:
+                return "restored";
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+                return "legacy_failure";
+            case DomainVerificationState.STATE_SYS_CONFIG:
+                return "system_configured";
+            default:
+                return String.valueOf(state);
+        }
+    }
+
+    /**
+     * For determining re-verify policy. This is hidden from the domain verification agent so that
+     * no behavior is made based on the result.
+     */
+    static boolean isDefault(@State int state) {
+        switch (state) {
+            case STATE_NO_RESPONSE:
+            case STATE_MIGRATED:
+            case STATE_RESTORED:
+                return true;
+            case STATE_SUCCESS:
+            case STATE_APPROVED:
+            case STATE_DENIED:
+            case STATE_LEGACY_FAILURE:
+            case STATE_SYS_CONFIG:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks if a state considers the corresponding domain to be successfully verified. The domain
+     * verification agent may use this to determine whether or not to re-verify a domain.
+     */
+    static boolean isVerified(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_SUCCESS:
+            case DomainVerificationState.STATE_APPROVED:
+            case DomainVerificationState.STATE_MIGRATED:
+            case DomainVerificationState.STATE_RESTORED:
+            case DomainVerificationState.STATE_SYS_CONFIG:
+                return true;
+            case DomainVerificationState.STATE_NO_RESPONSE:
+            case DomainVerificationState.STATE_DENIED:
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks if a state is modifiable by the domain verification agent. This is useful as the
+     * platform may add new state codes in newer versions, and older verification agents can use
+     * this method to determine if a state can be changed without having to be aware of what the new
+     * state means.
+     */
+    static boolean isModifiable(@DomainVerificationState.State int state) {
+        switch (state) {
+            case DomainVerificationState.STATE_NO_RESPONSE:
+            case DomainVerificationState.STATE_SUCCESS:
+            case DomainVerificationState.STATE_MIGRATED:
+            case DomainVerificationState.STATE_RESTORED:
+            case DomainVerificationState.STATE_LEGACY_FAILURE:
+                return true;
+            case DomainVerificationState.STATE_APPROVED:
+            case DomainVerificationState.STATE_DENIED:
+            case DomainVerificationState.STATE_SYS_CONFIG:
+                return false;
+            default:
+                return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+        }
+    }
+
+    /**
+     * Whether the state is migrated when updating a package. Generally this is only for states
+     * that maintain verification state or were set by an explicit user or developer action.
+     */
+    static boolean shouldMigrate(@State int state) {
+        switch (state) {
+            case STATE_SUCCESS:
+            case STATE_MIGRATED:
+            case STATE_RESTORED:
+            case STATE_APPROVED:
+            case STATE_DENIED:
+                return true;
+            case STATE_NO_RESPONSE:
+            case STATE_LEGACY_FAILURE:
+            case STATE_SYS_CONFIG:
+            case STATE_FIRST_VERIFIER_DEFINED:
+            default:
+                return false;
+        }
+    }
+
+    @DomainVerificationInfo.State
+    static int convertToInfoState(@State int internalState) {
+        if (internalState >= STATE_FIRST_VERIFIER_DEFINED) {
+            return internalState;
+        } else if (internalState == STATE_NO_RESPONSE) {
+            return DomainVerificationInfo.STATE_NO_RESPONSE;
+        } else if (internalState == STATE_SUCCESS) {
+            return DomainVerificationInfo.STATE_SUCCESS;
+        } else if (!isModifiable(internalState)) {
+            return DomainVerificationInfo.STATE_UNMODIFIABLE;
+        } else if (isVerified(internalState)) {
+            return DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED;
+        } else {
+            return DomainVerificationInfo.STATE_MODIFIABLE_UNVERIFIED;
+        }
+    }
 }
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
index 332b925..53205f3 100644
--- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -40,10 +40,10 @@
     @nullable
     List<DomainOwner> getOwnersForDomain(String domain, int userId);
 
-    void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
+    int setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
 
     void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
 
-    void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
+    int setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
             boolean enabled, int userId);
 }
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/IOnCompleteListener.aidl
similarity index 91%
rename from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
rename to core/java/android/net/IOnCompleteListener.aidl
index 7979afc..4bb89f6c 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/core/java/android/net/IOnCompleteListener.aidl
@@ -18,6 +18,6 @@
 package android.net;
 
 /** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
+oneway interface IOnCompleteListener {
     void onComplete();
 }
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/NetworkScore.aidl
similarity index 79%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to core/java/android/net/NetworkScore.aidl
index 7979afc..af12dcf 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/core/java/android/net/NetworkScore.aidl
@@ -1,6 +1,5 @@
 /**
- *
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,7 +16,5 @@
 
 package android.net;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+parcelable NetworkScore;
+
diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java
new file mode 100644
index 0000000..f478010
--- /dev/null
+++ b/core/java/android/net/NetworkScore.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Object representing the quality of a network as perceived by the user.
+ *
+ * A NetworkScore object represents the characteristics of a network that affects how good the
+ * network is considered for a particular use.
+ * @hide
+ */
+// TODO : @SystemApi when the implementation is complete
+public final class NetworkScore implements Parcelable {
+    // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
+    // a migration.
+    private final int mLegacyInt;
+
+    /** @hide */
+    NetworkScore(final int legacyInt) {
+        this.mLegacyInt = legacyInt;
+    }
+
+    private NetworkScore(@NonNull final Parcel in) {
+        mLegacyInt = in.readInt();
+    }
+
+    public int getLegacyInt() {
+        return mLegacyInt;
+    }
+
+    @Override
+    public String toString() {
+        return "Score(" + mLegacyInt + ")";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(mLegacyInt);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() {
+        @Override
+        @NonNull
+        public NetworkScore createFromParcel(@NonNull final Parcel in) {
+            return new NetworkScore(in);
+        }
+
+        @Override
+        @NonNull
+        public NetworkScore[] newArray(int size) {
+            return new NetworkScore[size];
+        }
+    };
+
+    /**
+     * A builder for NetworkScore.
+     */
+    public static final class Builder {
+        private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
+        private int mLegacyInt = INVALID_LEGACY_INT;
+
+        /**
+         * Sets the legacy int for this score.
+         *
+         * Do not rely on this. It will be gone by the time S is released.
+         *
+         * @param score the legacy int
+         * @return this
+         */
+        @NonNull
+        public Builder setLegacyInt(final int score) {
+            mLegacyInt = score;
+            return this;
+        }
+
+        /**
+         * Builds this NetworkScore.
+         * @return The built NetworkScore object.
+         */
+        @NonNull
+        public NetworkScore build() {
+            return new NetworkScore(mLegacyInt);
+        }
+    }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4c26e2f..fa6472e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -986,13 +986,13 @@
         public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which);
 
         /**
-         * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
+         * Returns the battery consumption (in microcoulombs) of bluetooth for this uid,
          * derived from on device power measurement data.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getScreenOnMeasuredBatteryConsumptionUC();
+        public abstract long getBluetoothMeasuredBatteryConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
@@ -1004,6 +1004,24 @@
         public abstract long getCpuMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
+         * derived from on device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getScreenOnMeasuredBatteryConsumptionUC();
+
+        /**
+         * Returns the battery consumption (in microcoulombs) of wifi for this uid,
+         * derived from on device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getWifiMeasuredBatteryConsumptionUC();
+
+        /**
          * Returns the battery consumption (in microcoulombs) used by this uid for each
          * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
          * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
@@ -2505,11 +2523,29 @@
     };
 
     /**
-     * Returned value if power data is unavailable
+     * Returned value if power data is unavailable.
      *
      * {@hide}
      */
-    public static final long POWER_DATA_UNAVAILABLE = -1;
+    public static final long POWER_DATA_UNAVAILABLE = -1L;
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on
+     * device power measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getBluetoothMeasuredBatteryConsumptionUC();
+
+    /**
+     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getCpuMeasuredBatteryConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
@@ -2530,13 +2566,13 @@
     public abstract long getScreenDozeMeasuredBatteryConsumptionUC();
 
     /**
-     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
-     * measurement data.
+     * Returns the battery consumption (in microcoulombs) of wifi, derived from on
+     * device power measurement data.
      * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
      *
      * {@hide}
      */
-    public abstract long getCpuMeasuredBatteryConsumptionUC();
+    public abstract long getWifiMeasuredBatteryConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) that each
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0d9f715..a2edc93 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -140,7 +140,7 @@
      */
     @UnsupportedAppUsage
     @TestApi
-    public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
+    public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1");
 
     /**
      * A hardware serial number, if available. Alphanumeric only, case-insensitive.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a19728c..2c4d130 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -197,6 +197,9 @@
      * application as the normal behavior.  Notifications that pop up and want
      * the device to be on are the exception; use this flag to be like them.
      * </p><p>
+     * Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any
+     * wake-up, including wake-ups triggered by wake locks.
+     * </p><p>
      * Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
      * </p>
      */
@@ -1299,6 +1302,9 @@
      * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT
      * default display group} is already off then nothing will happen.
      *
+     * <p>If the device is an Android TV playback device and the current active source on the
+     * HDMI-connected TV, it will attempt to turn off that TV via HDMI-CEC.
+     *
      * <p>
      * Overrides all the wake locks that are held.
      * This is what happens when the power key is pressed to turn off the screen.
@@ -1430,6 +1436,10 @@
      * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
      * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
      *
+     * <p>If the device is an Android TV playback device, it will attempt to turn on the
+     * HDMI-connected TV and become the current active source via the HDMI-CEC One Touch Play
+     * feature.
+     *
      * <p>
      * This is what happens when the power key is pressed to turn on the screen.
      * </p><p>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e75e224..ab1f688 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,8 +16,11 @@
 
 package android.os;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
@@ -108,6 +111,7 @@
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int VPN_UID = 1016;
 
     /**
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index df4ade0..d89c3d5 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -21,6 +21,7 @@
 import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -354,6 +355,23 @@
     }
 
     /**
+     * Performs {@code action} on each callback and associated cookie, calling {@link
+     * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
+     *
+     * @hide
+     */
+    public <C> void broadcast(BiConsumer<E, C> action) {
+        int itemCount = beginBroadcast();
+        try {
+            for (int i = 0; i < itemCount; i++) {
+                action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+            }
+        } finally {
+            finishBroadcast();
+        }
+    }
+
+    /**
      * Returns the number of registered callbacks. Note that the number of registered
      * callbacks may differ from the value returned by {@link #beginBroadcast()} since
      * the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
index 016cc2f..ad74a9f 100644
--- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -110,7 +110,7 @@
         }
         // Calculate energy used using PowerProfile.
         PowerProfile powerProfile = new PowerProfile(context);
-        final double rxIdleCurrent = powerProfile.getAveragePower(
+        final double idleCurrent = powerProfile.getAveragePower(
                 PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
         final double rxCurrent = powerProfile.getAveragePower(
                 PowerProfile.POWER_WIFI_CONTROLLER_RX);
@@ -121,7 +121,7 @@
 
         return (long) ((txDurationMillis * txCurrent
                 + rxDurationMillis * rxCurrent
-                + idleDurationMillis * rxIdleCurrent)
+                + idleDurationMillis * idleCurrent)
                 * voltage);
     }
 
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index bae36b29..177e422 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -41,6 +41,7 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.location.LocationManager;
 import android.media.AudioManager;
 import android.os.Build;
 import android.os.Handler;
@@ -50,12 +51,14 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.Immutable;
 import com.android.internal.util.CollectionUtils;
 
@@ -82,6 +85,8 @@
     public static final String KILL_APP_REASON_GIDS_CHANGED =
             "permission grant or revoke changed gids";
 
+    private static final String SYSTEM_PKG = "android";
+
     /**
      * Refuse to install package if groups of permissions are bad
      * - Permission groups should only be shared between apps sharing a certificate
@@ -857,6 +862,23 @@
     }
 
     /**
+     * Check if this package/op combination is exempted from indicators
+     * @return
+     * @hide
+     */
+    public static boolean isSpecialCaseShownIndicator(@NonNull Context context,
+            @NonNull String packageName) {
+
+        if (packageName.equals(SYSTEM_PKG)) {
+            return false;
+        }
+
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
+                false)
+                || packageName.equals(context.getString(R.string.config_systemSpeechRecognizer))
+                || context.getSystemService(LocationManager.class).isProviderPackage(packageName);
+    }
+    /**
      * Gets the list of packages that have permissions that specified
      * {@code requestDontAutoRevokePermissions=true} in their
      * {@code application} manifest declaration.
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 7e3a0f3..80a3e16 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -31,40 +31,25 @@
 import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
-import android.Manifest;
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.Attribution;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.icu.text.ListFormatter;
-import android.location.LocationManager;
 import android.media.AudioManager;
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.speech.RecognitionService;
-import android.speech.RecognizerIntent;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.internal.R;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * A helper which gets all apps which have used microphone, camera, and possible location
@@ -165,7 +150,7 @@
      * Constructor for PermissionUsageHelper
      * @param context The context from which to derive the package information
      */
-    public PermissionUsageHelper(Context context) {
+    public PermissionUsageHelper(@NonNull Context context) {
         mContext = context;
         mPkgManager = context.getPackageManager();
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
@@ -180,26 +165,10 @@
         return mUserContexts.get(user);
     }
 
-    // TODO ntmyren: Replace this with better check if this moves beyond teamfood
-    private boolean isAppPredictor(String packageName, UserHandle user) {
-        return shouldShowPermissionsHub() && getUserContext(user).getPackageManager()
-                .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    private boolean isSpeechRecognizerUsage(String op, String packageName) {
-        if (!OPSTR_RECORD_AUDIO.equals(op)) {
-            return false;
-        }
-
-        return packageName.equals(
-                mContext.getString(R.string.config_systemSpeechRecognizer));
-    }
-
     /**
      * @see PermissionManager.getIndicatorAppOpUsageData
      */
-    public List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
+    public @NonNull List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
         List<PermGroupUsage> usages = new ArrayList<>();
 
         if (!shouldShowIndicators()) {
@@ -215,9 +184,6 @@
         }
 
         Map<String, List<OpUsage>> rawUsages = getOpUsages(ops);
-        Set<List<PackageAttribution>> proxyChains = getProxyChains(rawUsages.get(MICROPHONE));
-        Map<PackageAttribution, CharSequence> packagesWithAttributionLabels =
-                getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains);
 
         ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
 
@@ -245,15 +211,8 @@
             boolean isPhone = false;
             String permGroup = usedPermGroups.get(permGroupNum);
 
-            Map<PackageAttribution, CharSequence> pkgAttrLabels = packagesWithAttributionLabels;
-            Set<List<PackageAttribution>> proxies = proxyChains;
-            if (!MICROPHONE.equals(permGroup)) {
-                pkgAttrLabels = new ArrayMap<>();
-                proxies = new ArraySet<>();
-            }
-
-            List<OpUsage> permUsages = removeDuplicatesAndProxies(rawUsages.get(permGroup),
-                    pkgAttrLabels.keySet(), proxies);
+            ArrayMap<OpUsage, CharSequence> usagesWithLabels =
+                    getUniqueUsagesWithLabels(rawUsages.get(permGroup));
 
             if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
                 isPhone = true;
@@ -263,11 +222,11 @@
                 permGroup = CAMERA;
             }
 
-            for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) {
-                OpUsage usage = permUsages.get(usageNum);
+            for (int usageNum = 0; usageNum < usagesWithLabels.size(); usageNum++) {
+                OpUsage usage = usagesWithLabels.keyAt(usageNum);
                 usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup,
                         usage.lastAccessTime, usage.isRunning, isPhone,
-                        packagesWithAttributionLabels.get(usage.toPackageAttr())));
+                        usagesWithLabels.valueAt(usageNum)));
             }
         }
 
@@ -297,7 +256,7 @@
         long recentThreshold = getRecentThreshold(now);
         long runningThreshold = getRunningThreshold(now);
         int opFlags = OP_FLAGS_ALL_TRUSTED;
-        Map<String, Map<PackageAttribution, OpUsage>> usages = new ArrayMap<>();
+        Map<String, Map<Integer, OpUsage>> usages = new ArrayMap<>();
 
         int numPkgOps = ops.size();
         for (int pkgOpNum = 0; pkgOpNum < numPkgOps; pkgOpNum++) {
@@ -326,9 +285,7 @@
 
                     if (packageName.equals(SYSTEM_PKG)
                             || (!shouldShowPermissionsHub()
-                            && !isUserSensitive(packageName, user, op)
-                            && !isLocationProvider(packageName, user)
-                            && !isSpeechRecognizerUsage(op, packageName))) {
+                            && !isUserSensitive(packageName, user, op))) {
                         continue;
                     }
 
@@ -339,20 +296,20 @@
                     AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags);
                     if (proxy != null && proxy.getPackageName() != null) {
                         proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(),
-                                proxy.getUid(), lastAccessTime, isRunning, null);
+                                op, proxy.getUid(), lastAccessTime, isRunning, null);
                     }
 
                     String permGroupName = getGroupForOp(op);
-                    OpUsage usage = new OpUsage(packageName, attributionTag, uid,
+                    OpUsage usage = new OpUsage(packageName, attributionTag, op, uid,
                             lastAccessTime, isRunning, proxyUsage);
 
-                    PackageAttribution packageAttr = usage.toPackageAttr();
+                    Integer packageAttr = usage.getPackageAttrHash();
                     if (!usages.containsKey(permGroupName)) {
-                        ArrayMap<PackageAttribution, OpUsage> map = new ArrayMap<>();
+                        ArrayMap<Integer, OpUsage> map = new ArrayMap<>();
                         map.put(packageAttr, usage);
                         usages.put(permGroupName, map);
                     } else {
-                        Map<PackageAttribution, OpUsage> permGroupUsages =
+                        Map<Integer, OpUsage> permGroupUsages =
                                 usages.get(permGroupName);
                         if (!permGroupUsages.containsKey(packageAttr)) {
                             permGroupUsages.put(packageAttr, usage);
@@ -374,380 +331,119 @@
         return flattenedUsages;
     }
 
-    /**
-     * Take the list of all usages, figure out any proxy chains, get all possible special
-     * attribution labels, and figure out which usages need to show a special label, if any.
-     *
-     * @param usages The raw permission usages
-     *
-     * @return A map of package + attribution (in the form of a PackageAttribution object) to
-     * trusted attribution label, if there is one
-     */
-    private ArrayMap<PackageAttribution, CharSequence> getTrustedAttributions(
-            List<OpUsage> usages, Set<List<PackageAttribution>> proxyChains) {
-        ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>();
-        if (usages == null) {
-            return attributions;
-        }
-
-        Map<PackageAttribution, CharSequence> trustedLabels =
-                getTrustedAttributionLabels(usages);
-
-        for (List<PackageAttribution> chain : proxyChains) {
-            // If this chain is empty, or has only one link, then do not show any special labels
-            if (chain.size() <= 1) {
-                continue;
-            }
-
-            // If the last link in the chain is not user sensitive, do not show it.
-            boolean lastLinkIsUserSensitive = false;
-            for (int i = 0; i < usages.size(); i++) {
-                PackageAttribution lastLink = chain.get(chain.size() - 1);
-                if (lastLink.equals(usages.get(i).toPackageAttr())) {
-                    lastLinkIsUserSensitive = true;
-                    break;
-                }
-            }
-            if (!lastLinkIsUserSensitive) {
-                continue;
-            }
-
-            List<CharSequence> labels = new ArrayList<>();
-            for (int i = 0; i < chain.size(); i++) {
-                // If this is the last link in the proxy chain, assign it the series of labels
-                // Else, if it has a special label, add that label
-                // Else, if there are no other apps in the remaining part of the chain which
-                // have the same package name, add the app label
-                // If it is not the last link in the chain, remove its attribution
-                PackageAttribution attr = chain.get(i);
-                CharSequence trustedLabel = trustedLabels.get(attr);
-                if (i == chain.size() - 1) {
-                    attributions.put(attr, formatLabelList(labels));
-                } else if (trustedLabel != null && !labels.contains(trustedLabel)) {
-                    labels.add(trustedLabel);
-                    trustedLabels.remove(attr);
-                } else {
-                    boolean remainingChainHasPackage = false;
-                    for (int attrNum = i + 1; attrNum < chain.size() - 1; attrNum++) {
-                        if (chain.get(i).packageName.equals(attr.packageName)) {
-                            remainingChainHasPackage = true;
-                            break;
-                        }
-                    }
-                    if (!remainingChainHasPackage) {
-                        try {
-                            ApplicationInfo appInfo = mPkgManager.getApplicationInfoAsUser(
-                                    attr.packageName, 0, attr.getUser());
-                            CharSequence appLabel = appInfo.loadLabel(
-                                    getUserContext(attr.getUser()).getPackageManager());
-                            labels.add(appLabel);
-                        } catch (PackageManager.NameNotFoundException e) {
-                            // Do nothing
-                        }
-                    }
-                }
-            }
-        }
-
-        for (PackageAttribution attr : trustedLabels.keySet()) {
-            attributions.put(attr, trustedLabels.get(attr));
-        }
-
-        return attributions;
-    }
-
     private CharSequence formatLabelList(List<CharSequence> labels) {
         return ListFormatter.getInstance().format(labels);
     }
 
-    /**
-     * Get all chains of proxy usages. A proxy chain is defined as one usage at the root, then
-     * further proxy usages, where the app and attribution tag of the proxy in the proxy usage
-     * matches the previous usage in the chain.
-     *
-     * @param usages The permission usages
-     *
-     * @return A set of lists of package attributions. One list represents a chain of proxy usages,
-     * with the start of the chain (the usage without a proxy) at position 0, and each usage down
-     * the chain has the previous one listed as a proxy usage.
-     */
-    private Set<List<PackageAttribution>> getProxyChains(List<OpUsage> usages) {
+    private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
+        ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
+
         if (usages == null) {
-            return new ArraySet<>();
+            return usagesAndLabels;
         }
 
-        ArrayMap<PackageAttribution, ArrayList<PackageAttribution>> proxyChains = new ArrayMap<>();
-        // map of usages that still need to be removed, or added to a chain
-        ArrayMap<PackageAttribution, OpUsage> remainingUsages = new ArrayMap<>();
-        // map of usage.proxy -> usage, telling us if a usage is a proxy
-        ArrayMap<PackageAttribution, PackageAttribution> proxies = new ArrayMap<>();
+        ArrayMap<Integer, OpUsage> allUsages = new ArrayMap<>();
+        // map of uid -> most recent non-proxy-related usage for that uid.
+        ArrayMap<Integer, OpUsage> mostRecentUsages = new ArrayMap<>();
+        // set of all uids involved in a proxy usage
+        ArraySet<Integer> proxyUids = new ArraySet<>();
+        // map of usage -> list of proxy app labels
+        ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
+        // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
+        ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
         for (int i = 0; i < usages.size(); i++) {
             OpUsage usage = usages.get(i);
-            remainingUsages.put(usage.toPackageAttr(), usage);
+            allUsages.put(usage.getPackageAttrHash(), usage);
             if (usage.proxy != null) {
-                proxies.put(usage.proxy.toPackageAttr(), usage.toPackageAttr());
+                proxies.put(usage.proxy.getPackageAttrHash(), usage);
             }
         }
 
-        // find all possible end points for chains
-        List<PackageAttribution> keys = new ArrayList<>(remainingUsages.keySet());
-        for (int usageNum = 0; usageNum < remainingUsages.size(); usageNum++) {
-            OpUsage usage = remainingUsages.get(keys.get(usageNum));
+        // find all possible end points for chains, and find the most recent of the rest of the uses
+        for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
+            OpUsage usage = usages.get(usageNum);
             if (usage == null) {
                 continue;
             }
-            PackageAttribution usageAttr = usage.toPackageAttr();
+
+            int usageAttr = usage.getPackageAttrHash();
             // If this usage has a proxy, but is not a proxy, it is the end of a chain.
-            // If it has no proxy, and isn't a proxy, remove it.
             if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
-                ArrayList<PackageAttribution> proxyList = new ArrayList<>();
-                proxyList.add(usageAttr);
-                proxyChains.put(usageAttr, proxyList);
-            } else if (!proxies.containsKey(usageAttr) && usage.proxy == null) {
-                remainingUsages.remove(keys.get(usageNum));
+                proxyLabels.put(usage, new ArrayList<>());
+                proxyUids.add(usage.uid);
+            }
+            if (!mostRecentUsages.containsKey(usage.uid) || usage.lastAccessTime
+                    > mostRecentUsages.get(usage.uid).lastAccessTime) {
+                mostRecentUsages.put(usage.uid, usage);
             }
         }
 
-        // assemble the chains in reverse order, then invert them
-        for (int numStart = 0; numStart < proxyChains.size(); numStart++) {
-            PackageAttribution currPackageAttr = proxyChains.keyAt(numStart);
-            ArrayList<PackageAttribution> proxyChain = proxyChains.get(currPackageAttr);
-            OpUsage currentUsage = remainingUsages.get(currPackageAttr);
-            if (currentUsage == null || proxyChain == null) {
+        // get all the proxy labels
+        for (int numStart = 0; numStart < proxyLabels.size(); numStart++) {
+            OpUsage start = proxyLabels.keyAt(numStart);
+            // Remove any non-proxy usage for the starting uid
+            mostRecentUsages.remove(start.uid);
+            OpUsage currentUsage = proxyLabels.keyAt(numStart);
+            ArrayList<CharSequence> proxyLabelList = proxyLabels.get(currentUsage);
+            if (currentUsage == null || proxyLabelList == null) {
                 continue;
             }
+            int iterNum = 0;
+            int maxUsages = allUsages.size();
             while (currentUsage.proxy != null) {
-                currPackageAttr = currentUsage.proxy.toPackageAttr();
-                currentUsage = remainingUsages.get(currPackageAttr);
 
-                boolean invalidState = false;
-                for (int chainNum = 0; chainNum < proxyChain.size(); chainNum++) {
-                    if (currentUsage == null || proxyChain.get(chainNum).equals(currPackageAttr)) {
-                        // either our current value is not in the usage list, or we have a cycle
-                        invalidState = true;
+                if (allUsages.containsKey(currentUsage.proxy.getPackageAttrHash())) {
+                    currentUsage = allUsages.get(currentUsage.proxy.getPackageAttrHash());
+                } else {
+                    // We are missing the proxy usage. This may be because it's a one-step trusted
+                    // proxy. Check if we should show the proxy label, and show it, if so.
+                    OpUsage proxy = currentUsage.proxy;
+                    if (PermissionManager.isSpecialCaseShownIndicator(mContext, proxy.packageName)
+                            || isUserSensitive(proxy.packageName, proxy.getUser(), proxy.op)) {
+                        currentUsage = proxy;
+                        // We've effectively added one usage, so increment the max number of usages
+                        maxUsages++;
+                    } else {
                         break;
                     }
                 }
 
-                if (invalidState) {
+
+                if (currentUsage == null || iterNum == maxUsages
+                        || currentUsage.getPackageAttrHash() == start.getPackageAttrHash()) {
+                    // We have an invalid state, or a cycle, so break
                     break;
                 }
 
-                proxyChain.add(currPackageAttr);
-            }
-            // invert the lists, so the element without a proxy is first on the list
-            Collections.reverse(proxyChain);
-        }
-
-        return new ArraySet<>(proxyChains.values());
-    }
-
-    /**
-     * Gets all trusted proxied voice IME and voice recognition microphone uses, and get the
-     * label needed to display with it, as well as information about the proxy whose label is being
-     * shown, if applicable.
-     *
-     * @param usages The permission usages
-     *
-     * @return A map of package attribution -> the attribution label for that package attribution,
-     * if applicable
-     */
-    private Map<PackageAttribution, CharSequence> getTrustedAttributionLabels(
-            List<OpUsage> usages) {
-        List<UserHandle> users = new ArrayList<>();
-        for (int i = 0; i < usages.size(); i++) {
-            UserHandle user = UserHandle.getUserHandleForUid(usages.get(i).uid);
-            if (!users.contains(user)) {
-                users.add(user);
-            }
-        }
-
-        Map<PackageAttribution, CharSequence> trustedLabels = new ArrayMap<>();
-        for (int userNum = 0; userNum < users.size(); userNum++) {
-            UserHandle user = users.get(userNum);
-            Context userContext = mContext.createContextAsUser(user, 0);
-
-            // Get all voice IME labels
-            Map<String, CharSequence> voiceInputs = new ArrayMap<>();
-            List<InputMethodInfo> inputs = userContext.getSystemService(InputMethodManager.class)
-                    .getEnabledInputMethodList();
-            for (int inputNum = 0; inputNum < inputs.size(); inputNum++) {
-                InputMethodInfo input = inputs.get(inputNum);
-                for (int subtypeNum = 0; subtypeNum < input.getSubtypeCount(); subtypeNum++) {
-                    if (VOICE_IME_SUBTYPE.equals(input.getSubtypeAt(subtypeNum).getMode())) {
-                        voiceInputs.put(input.getPackageName(), input.getServiceInfo()
-                                .loadUnsafeLabel(userContext.getPackageManager()));
-                        break;
+                proxyUids.add(currentUsage.uid);
+                try {
+                    PackageManager userPkgManager =
+                            getUserContext(currentUsage.getUser()).getPackageManager();
+                    ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
+                            currentUsage.packageName, 0);
+                    CharSequence appLabel = appInfo.loadLabel(userPkgManager);
+                    // If we don't already have the app label, and it's not the same as the main
+                    // app, add it
+                    if (!proxyLabelList.contains(appLabel)
+                            && !currentUsage.packageName.equals(start.packageName)) {
+                        proxyLabelList.add(appLabel);
                     }
+                } catch (PackageManager.NameNotFoundException e) {
+                    // Ignore
                 }
+                iterNum++;
             }
+            usagesAndLabels.put(start,
+                    proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
+        }
 
-            // Get the currently selected recognizer from the secure setting
-            String recognitionPackageName = Settings.Secure.getString(
-                    userContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE);
-            if (recognitionPackageName == null) {
-                continue;
-            }
-            recognitionPackageName =
-                    ComponentName.unflattenFromString(recognitionPackageName).getPackageName();
-            Map<String, CharSequence> recognizers = new ArrayMap<>();
-            List<ResolveInfo> availableRecognizers = mPkgManager.queryIntentServicesAsUser(
-                    new Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
-                    user.getIdentifier());
-            for (int recogNum = 0; recogNum < availableRecognizers.size(); recogNum++) {
-                ResolveInfo info = availableRecognizers.get(recogNum);
-                if (recognitionPackageName.equals(info.serviceInfo.packageName)) {
-                    recognizers.put(recognitionPackageName, info.serviceInfo.loadUnsafeLabel(
-                            userContext.getPackageManager()));
-                }
-            }
-
-            Map<String, CharSequence> recognizerIntents = new ArrayMap<>();
-            List<ResolveInfo> availableRecognizerIntents = mPkgManager.queryIntentActivitiesAsUser(
-                    new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
-                    PackageManager.GET_META_DATA, user);
-            for (int recogNum = 0; recogNum < availableRecognizerIntents.size(); recogNum++) {
-                ResolveInfo info = availableRecognizerIntents.get(recogNum);
-                if (info.activityInfo == null) {
-                    continue;
-                }
-                String pkgName = info.activityInfo.packageName;
-                if (recognitionPackageName.equals(pkgName) && recognizers.containsKey(pkgName)) {
-                    recognizerIntents.put(pkgName, recognizers.get(pkgName));
-                }
-            }
-            for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
-                setTrustedAttrsForAccess(usages.get(usageNum), user, false, voiceInputs,
-                        trustedLabels);
-                setTrustedAttrsForAccess(usages.get(usageNum), user, false, recognizerIntents,
-                        trustedLabels);
-                setTrustedAttrsForAccess(usages.get(usageNum), user, true, recognizers,
-                        trustedLabels);
+        for (int uid : mostRecentUsages.keySet()) {
+            if (!proxyUids.contains(uid)) {
+                usagesAndLabels.put(mostRecentUsages.get(uid), null);
             }
         }
 
-        return trustedLabels;
-    }
-
-    private void setTrustedAttrsForAccess(OpUsage opUsage, UserHandle currUser, boolean getProxy,
-            Map<String, CharSequence> trustedMap, Map<PackageAttribution, CharSequence> toSetMap) {
-        OpUsage usage = opUsage;
-        if (getProxy) {
-            usage = opUsage.proxy;
-        }
-
-        if (usage == null || !usage.getUser().equals(currUser)
-                || !trustedMap.containsKey(usage.packageName)) {
-            return;
-        }
-
-        CharSequence label = getAttributionLabel(usage);
-        if (trustedMap.get(usage.packageName).equals(label)) {
-            toSetMap.put(opUsage.toPackageAttr(), label);
-        }
-    }
-
-    private CharSequence getAttributionLabel(OpUsage usage) {
-        if (usage.attributionTag == null) {
-            return null;
-        }
-
-        PackageInfo pkgInfo;
-        try {
-            pkgInfo = mPkgManager.getPackageInfoAsUser(usage.packageName,
-                    PackageManager.GET_ATTRIBUTIONS, usage.getUser().getIdentifier());
-            if (pkgInfo.attributions == null || pkgInfo.attributions.length == 0) {
-                return null;
-            }
-            for (int attrNum = 0; attrNum < pkgInfo.attributions.length; attrNum++) {
-                Attribution attr = pkgInfo.attributions[attrNum];
-                if (usage.attributionTag.equals(attr.getTag())) {
-                    return mContext.createPackageContextAsUser(usage.packageName, 0,
-                            usage.getUser()).getString(attr.getLabel());
-                }
-            }
-            return null;
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    /**
-     * If we have multiple usages of a
-     * @param rawUsages The list of all usages that we wish to
-     * @param specialAttributions A set of all usages that have a special label
-     * @param proxies A list of proxy chains- all links but the last on the chain should be removed,
-     *                if the last link has a special label
-     * @return A list of usages without duplicates or proxy usages.
-     */
-    private List<OpUsage> removeDuplicatesAndProxies(List<OpUsage> rawUsages,
-            Set<PackageAttribution> specialAttributions,
-            Set<List<PackageAttribution>> proxies) {
-        List<OpUsage> deDuped = new ArrayList<>();
-        if (rawUsages == null) {
-            return deDuped;
-        }
-
-        List<PackageAttribution> toRemoveProxies = new ArrayList<>();
-        for (List<PackageAttribution> proxyList: proxies) {
-            PackageAttribution lastLink = proxyList.get(proxyList.size() - 1);
-            if (!specialAttributions.contains(lastLink)) {
-                continue;
-            }
-            for (int proxyNum = 0; proxyNum < proxyList.size(); proxyNum++) {
-                if (!proxyList.get(proxyNum).equals(lastLink)) {
-                    toRemoveProxies.add(proxyList.get(proxyNum));
-                }
-            }
-        }
-
-        for (int usageNum = 0; usageNum < rawUsages.size(); usageNum++) {
-            OpUsage usage = rawUsages.get(usageNum);
-
-            // If this attribution is a proxy, remove it
-            if (toRemoveProxies.contains(usage.toPackageAttr())) {
-                continue;
-            }
-
-            // If this attribution has a special attribution, do not remove it
-            if (specialAttributions.contains(usage.toPackageAttr())) {
-                deDuped.add(usage);
-                continue;
-            }
-
-
-            // Search the rest of the list for usages with the same UID. If this is the most recent
-            // usage for that uid, keep it. Otherwise, remove it
-            boolean isMostRecentForUid = true;
-            for (int otherUsageNum = 0; otherUsageNum < rawUsages.size(); otherUsageNum++) {
-                // Do not compare this usage to itself
-                if (otherUsageNum == usageNum) {
-                    continue;
-                }
-
-                OpUsage otherUsage = rawUsages.get(otherUsageNum);
-                if (otherUsage.uid == usage.uid) {
-                    if (otherUsage.isRunning && !usage.isRunning) {
-                        isMostRecentForUid = false;
-                    } else if (usage.isRunning
-                            && otherUsage.lastAccessTime >= usage.lastAccessTime) {
-                        isMostRecentForUid = false;
-                    } else if (otherUsage.lastAccessTime >= usage.lastAccessTime) {
-                        isMostRecentForUid = false;
-                    }
-
-                    if (!isMostRecentForUid) {
-                        break;
-                    }
-                }
-            }
-
-            if (isMostRecentForUid) {
-                deDuped.add(usage);
-            }
-        }
-
-        return deDuped;
+        return usagesAndLabels;
     }
 
     private boolean isUserSensitive(String packageName, UserHandle user, String op) {
@@ -763,11 +459,6 @@
         return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
     }
 
-    private boolean isLocationProvider(String packageName, UserHandle user) {
-        return getUserContext(user)
-                .getSystemService(LocationManager.class).isProviderPackage(packageName);
-    }
-
     /**
      * Represents the usage of an App op by a particular package and attribution
      */
@@ -775,62 +466,45 @@
 
         public final String packageName;
         public final String attributionTag;
+        public final String op;
         public final int uid;
         public final long lastAccessTime;
         public final OpUsage proxy;
         public final boolean isRunning;
 
-        OpUsage(String packageName, String attributionTag, int uid, long lastAccessTime,
+        OpUsage(String packageName, String attributionTag, String op, int uid, long lastAccessTime,
                 boolean isRunning, OpUsage proxy) {
-            this.isRunning = isRunning;
             this.packageName = packageName;
             this.attributionTag = attributionTag;
+            this.op = op;
             this.uid = uid;
             this.lastAccessTime = lastAccessTime;
+            this.isRunning = isRunning;
             this.proxy = proxy;
         }
 
-        public PackageAttribution toPackageAttr() {
-            return new PackageAttribution(packageName, attributionTag, uid);
-        }
-
         public UserHandle getUser() {
             return UserHandle.getUserHandleForUid(uid);
         }
-    }
 
-    /**
-     * A unique identifier for one package attribution, made up of attribution tag, package name
-     * and user
-     */
-    private static class PackageAttribution {
-        public final String packageName;
-        public final String attributionTag;
-        public final int uid;
-
-        PackageAttribution(String packageName, String attributionTag, int uid) {
-            this.packageName = packageName;
-            this.attributionTag = attributionTag;
-            this.uid = uid;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof PackageAttribution)) {
-                return false;
-            }
-            PackageAttribution other = (PackageAttribution) obj;
-            return Objects.equals(packageName, other.packageName) && Objects.equals(attributionTag,
-                    other.attributionTag) && Objects.equals(uid, other.uid);
+        public int getPackageAttrHash() {
+            return Objects.hash(packageName, attributionTag, uid);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(packageName, attributionTag, uid);
+            return Objects.hash(packageName, attributionTag, op, uid, lastAccessTime, isRunning);
         }
 
-        public UserHandle getUser() {
-            return UserHandle.getUserHandleForUid(uid);
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof OpUsage)) {
+                return false;
+            }
+            OpUsage other = (OpUsage) obj;
+            return Objects.equals(packageName, other.packageName) && Objects.equals(attributionTag,
+                    other.attributionTag) && Objects.equals(op, other.op) && uid == other.uid
+                    && lastAccessTime == other.lastAccessTime && isRunning == other.isRunning;
         }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5d139d9..52bc39c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4126,7 +4126,6 @@
          * unset or a match is not made, only the standard color modes will be restored.
          * @hide
          */
-        @Readable
         public static final String DISPLAY_COLOR_MODE_VENDOR_HINT =
                 "display_color_mode_vendor_hint";
 
@@ -6163,7 +6162,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String CAMERA_AUTOROTATE = "camera_autorotate";
 
         /**
@@ -6724,7 +6722,6 @@
          * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update.
          * @hide
          */
-        @Readable
         public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED =
                 "location_time_zone_detection_enabled";
 
@@ -7515,7 +7512,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String REDUCE_BRIGHT_COLORS_ACTIVATED =
                 "reduce_bright_colors_activated";
 
@@ -7525,7 +7521,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String REDUCE_BRIGHT_COLORS_LEVEL =
                 "reduce_bright_colors_level";
 
@@ -7534,7 +7529,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS =
                 "reduce_bright_colors_persist_across_reboots";
 
@@ -8245,7 +8239,6 @@
          * @see #MATCH_CONTENT_FRAMERATE_ALWAYS
          * @hide
          */
-        @Readable
         public static final String MATCH_CONTENT_FRAME_RATE =
                 "match_content_frame_rate";
 
@@ -8373,7 +8366,6 @@
          * {@link Display.STATE_OFF} and {@link Display.STATE_DOZE}.
          * @hide
          */
-        @Readable
         public static final String DOZE_QUICK_PICKUP_GESTURE = "doze_quick_pickup_gesture";
 
         /**
@@ -8479,7 +8471,6 @@
          * For user preference if swipe bottom to expand notification gesture enabled.
          * @hide
          */
-        @Readable
         public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
                 "swipe_bottom_to_notification_enabled";
 
@@ -8487,28 +8478,24 @@
          * For user preference if One-Handed Mode enabled.
          * @hide
          */
-        @Readable
         public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
 
         /**
          * For user preference if One-Handed Mode timeout.
          * @hide
          */
-        @Readable
         public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
 
         /**
          * For user taps app to exit One-Handed Mode.
          * @hide
          */
-        @Readable
         public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
 
         /**
          * Internal use, one handed mode tutorial showed times.
          * @hide
          */
-        @Readable
         public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT =
                 "one_handed_tutorial_show_count";
 
@@ -8534,7 +8521,6 @@
          * The last computed night mode bool the last time the phone was on
          * @hide
          */
-        @Readable
         public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed";
 
         /**
@@ -8953,7 +8939,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled";
 
         /**
@@ -8961,7 +8946,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
                 "emergency_gesture_sound_enabled";
 
@@ -9694,7 +9678,6 @@
          * @see Settings.Secure#MEDIA_CONTROLS_RESUME
          * @hide
          */
-        @Readable
         public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
 
         /**
@@ -9742,7 +9725,6 @@
          * @hide
          */
         @TestApi
-        @Readable
         public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
                 "accessibility_magnification_capability";
 
@@ -9752,7 +9734,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
                 "accessibility_show_window_magnification_prompt";
 
@@ -9769,7 +9750,6 @@
          * @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
          * @hide
          */
-        @Readable
         public static final String ACCESSIBILITY_BUTTON_MODE =
                 "accessibility_button_mode";
 
@@ -9798,7 +9778,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_SIZE =
                 "accessibility_floating_menu_size";
 
@@ -9811,7 +9790,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE =
                 "accessibility_floating_menu_icon_type";
 
@@ -9820,7 +9798,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED =
                 "accessibility_floating_menu_fade_enabled";
 
@@ -9830,7 +9807,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY =
                 "accessibility_floating_menu_opacity";
 
@@ -9839,7 +9815,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
 
         /**
@@ -9863,7 +9838,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
                 "reminder_exp_learning_time_elapsed";
 
@@ -9872,7 +9846,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
                 "reminder_exp_learning_event_count";
 
@@ -10581,7 +10554,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH =
                 "wm_display_settings_path";
 
@@ -10761,7 +10733,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP =
                 "hdmi_control_send_standby_on_sleep";
 
@@ -14439,7 +14410,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
                 "euicc_switch_slot_timeout_millis";
 
@@ -14449,7 +14419,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
                 "enable_multi_slot_timeout_millis";
 
@@ -14573,7 +14542,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
                 "force_non_debuggable_final_build_for_compat";
 
@@ -14653,7 +14621,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
 
         /**
@@ -15336,7 +15303,6 @@
          * The value 1 - enable, 0 - disable
          * @hide
          */
-        @Readable
         public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled";
 
         /**
@@ -15579,7 +15545,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist";
 
         /**
@@ -15784,7 +15749,6 @@
          * 1: Enabled
          * @hide
          */
-        @Readable
         public static final String SHOW_PEOPLE_SPACE = "show_people_space";
 
         /**
@@ -15795,7 +15759,6 @@
          * 2: All conversations
          * @hide
          */
-        @Readable
         public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
                 "people_space_conversation_type";
 
@@ -15806,7 +15769,6 @@
          * 1: Enabled
          * @hide
          */
-        @Readable
         public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss";
 
         /**
@@ -15821,7 +15783,6 @@
          * 1: Enabled (All apps will receive the new rules)
          * @hide
          */
-        @Readable
         public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules";
 
         /**
@@ -15836,7 +15797,6 @@
          * <p>See {@link android.app.Notification.DevFlags} for more details.
          * @hide
          */
-        @Readable
         public static final String FULLY_CUSTOM_VIEW_NOTIF_DECORATION =
                 "fully_custom_view_notif_decoration";
 
@@ -15850,7 +15810,6 @@
          * <p>See {@link android.app.Notification.DevFlags} for more details.
          * @hide
          */
-        @Readable
         public static final String DECORATED_CUSTOM_VIEW_NOTIF_DECORATION =
                 "decorated_custom_view_notif_decoration";
 
@@ -15867,7 +15826,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
 
         /**
@@ -15893,7 +15851,6 @@
          *
          * @hide
          */
-        @Readable
         public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
                 "maximum_obscuring_opacity_for_touch";
 
@@ -15906,7 +15863,6 @@
          * 1: enabled
          * @hide
          */
-        @Readable
         public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
     }
 
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index a0875a2..d000000 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -1353,10 +1353,7 @@
 
     /**
      * Interface for current physical channel configuration listener.
-     *
-     * @hide
      */
-    @SystemApi
     public interface PhysicalChannelConfigListener {
         /**
          * Callback invoked when the current physical channel configuration has changed
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index 7f6c4b4..d347f31 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -17,6 +17,7 @@
 package android.view.translation;
 
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.view.autofill.AutofillId;
 import android.view.translation.TranslationSpec;
 import com.android.internal.os.IResultReceiver;
@@ -40,4 +41,7 @@
     void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
          in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
          int userId);
+
+    void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
+    void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
 }
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index 7c73e70..852ffe8 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -16,28 +16,36 @@
 
 package android.view.translation;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.assist.ActivityId;
 import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
 import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
 import android.view.View;
 import android.view.autofill.AutofillId;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
+// TODO(b/178044703): Describe what UI Translation is.
 /**
  * The {@link UiTranslationManager} class provides ways for apps to use the ui translation
  * function in framework.
- *
- * @hide
  */
-@SystemApi
 public final class UiTranslationManager {
 
     private static final String TAG = "UiTranslationManager";
@@ -88,6 +96,14 @@
     public @interface UiTranslationState {
     }
 
+    // Keys for the data transmitted in the internal UI Translation state callback.
+    /** @hide */
+    public static final String EXTRA_STATE = "state";
+    /** @hide */
+    public static final String EXTRA_SOURCE_LOCALE = "source_locale";
+    /** @hide */
+    public static final String EXTRA_TARGET_LOCALE = "target_locale";
+
     @NonNull
     private final Context mContext;
 
@@ -111,9 +127,12 @@
      * @param destSpec {@link TranslationSpec} for the translated data.
      * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
      * @param taskId the Activity Task id which needs ui translation
+     *
+     * @hide
      */
     // TODO, hide the APIs
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void startTranslation(@NonNull TranslationSpec sourceSpec,
             @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
             int taskId) {
@@ -141,8 +160,11 @@
      * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
      * @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
      *         {@link android.app.assist.ActivityId#getToken()} is {@code null}
+     *
+     * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void startTranslation(@NonNull TranslationSpec sourceSpec,
             @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
             @NonNull ActivityId activityId) {
@@ -171,9 +193,12 @@
      * NOTE: Please use {@code finishTranslation(ActivityId)} instead.
      *
      * @param taskId the Activity Task id which needs ui translation
+     *
+     * @hide
      */
     // TODO, hide the APIs
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void finishTranslation(int taskId) {
         try {
             mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
@@ -191,8 +216,11 @@
      * @param activityId the identifier for the Activity which needs ui translation
      * @throws NullPointerException the activityId or
      *         {@link android.app.assist.ActivityId#getToken()} is {@code null}
+     *
+     * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void finishTranslation(@NonNull ActivityId activityId) {
         try {
             Objects.requireNonNull(activityId);
@@ -212,9 +240,12 @@
      * NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
      *
      * @param taskId the Activity Task id which needs ui translation
+     *
+     * @hide
      */
     // TODO, hide the APIs
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void pauseTranslation(int taskId) {
         try {
             mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
@@ -232,8 +263,11 @@
      * @param activityId the identifier for the Activity which needs ui translation
      * @throws NullPointerException the activityId or
      *         {@link android.app.assist.ActivityId#getToken()} is {@code null}
+     *
+     * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void pauseTranslation(@NonNull ActivityId activityId) {
         try {
             Objects.requireNonNull(activityId);
@@ -253,9 +287,12 @@
      * NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
      *
      * @param taskId the Activity Task id which needs ui translation
+     *
+     * @hide
      */
     // TODO, hide the APIs
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void resumeTranslation(int taskId) {
         try {
             mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
@@ -273,8 +310,11 @@
      * @param activityId the identifier for the Activity which needs ui translation
      * @throws NullPointerException the activityId or
      *         {@link android.app.assist.ActivityId#getToken()} is {@code null}
+     *
+     * @hide
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+    @SystemApi
     public void resumeTranslation(@NonNull ActivityId activityId) {
         try {
             Objects.requireNonNull(activityId);
@@ -286,4 +326,105 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    // TODO(b/178044703): Fix the View API link when it becomes public.
+    /**
+     * Register for notifications of UI Translation state changes on the foreground activity. This
+     * is available to the owning application itself and also the current input method.
+     * <p>
+     * The application whose UI is being translated can use this to customize the UI Translation
+     * behavior in ways that aren't made easy by methods like
+     * View#onCreateTranslationRequest().
+     * <p>
+     * Input methods can use this to offer complementary features to UI Translation; for example,
+     * enabling outgoing message translation when the system is translating incoming messages in a
+     * communication app.
+     *
+     * @param callback the callback to register for receiving the state change
+     *         notifications
+     */
+    public void registerUiTranslationStateCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull UiTranslationStateCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        synchronized (mCallbacks) {
+            if (mCallbacks.containsKey(callback)) {
+                Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;"
+                        + " ignoring.");
+                return;
+            }
+            final IRemoteCallback remoteCallback =
+                    new UiTranslationStateRemoteCallback(executor, callback);
+            try {
+                mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mCallbacks.put(callback, remoteCallback);
+        }
+    }
+
+    /**
+     * Unregister {@code callback}.
+     *
+     * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)
+     */
+    public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) {
+        Objects.requireNonNull(callback);
+
+        synchronized (mCallbacks) {
+            final IRemoteCallback remoteCallback = mCallbacks.get(callback);
+            if (remoteCallback == null) {
+                Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring.");
+                return;
+            }
+            try {
+                mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mCallbacks.remove(callback);
+        }
+    }
+
+    @NonNull
+    @GuardedBy("mCallbacks")
+    private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();
+
+    private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub {
+        private final Executor mExecutor;
+        private final UiTranslationStateCallback mCallback;
+
+        UiTranslationStateRemoteCallback(Executor executor,
+                UiTranslationStateCallback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void sendResult(Bundle bundle) {
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> onStateChange(bundle)));
+        }
+
+        private void onStateChange(Bundle bundle) {
+            int state = bundle.getInt(EXTRA_STATE);
+            switch (state) {
+                case STATE_UI_TRANSLATION_STARTED:
+                case STATE_UI_TRANSLATION_RESUMED:
+                    mCallback.onStarted(
+                            bundle.getString(EXTRA_SOURCE_LOCALE),
+                            bundle.getString(EXTRA_TARGET_LOCALE));
+                    break;
+                case STATE_UI_TRANSLATION_PAUSED:
+                    mCallback.onPaused();
+                    break;
+                case STATE_UI_TRANSLATION_FINISHED:
+                    mCallback.onFinished();
+                    break;
+                default:
+                    Log.wtf(TAG, "Unexpected translation state:" + state);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java
new file mode 100644
index 0000000..1946b70
--- /dev/null
+++ b/core/java/android/view/translation/UiTranslationStateCallback.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for listening to UI Translation state changes. See {@link
+ * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
+ */
+public interface UiTranslationStateCallback {
+
+    /**
+     * The system is requesting translation of the UI from {@code sourceLocale} to {@code
+     * targetLocale}.
+     * <p>
+     * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
+     * changed; or called again after {@link #onPaused()}.
+     */
+    void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);
+
+    /**
+     * The system is requesting that the application temporarily show the UI contents in their
+     * original language.
+     */
+    void onPaused();
+
+    /**
+     * The UI Translation session has ended.
+     */
+    void onFinished();
+}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 05c0047..3c41112 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -69,7 +69,7 @@
      * @hide
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BASE)
     public static final long USE_STRETCH_EDGE_EFFECT_BY_DEFAULT = 171228096L;
 
     /**
@@ -354,13 +354,14 @@
         mDistance = Math.max(0f, mPullDistance);
         mVelocity = 0;
 
-        final float absdd = Math.abs(deltaDistance);
-        mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
-                mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
-
         if (mPullDistance == 0) {
             mGlowScaleY = mGlowScaleYStart = 0;
+            mGlowAlpha = mGlowAlphaStart = 0;
         } else {
+            final float absdd = Math.abs(deltaDistance);
+            mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
+                    mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+
             final float scale = (float) (Math.max(0, 1 - 1 /
                     Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d);
 
@@ -698,7 +699,9 @@
 
         mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
         mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
-        mDistance = calculateDistanceFromGlowValues(mGlowScaleY, mGlowAlpha);
+        if (mState != STATE_PULL) {
+            mDistance = calculateDistanceFromGlowValues(mGlowScaleY, mGlowAlpha);
+        }
         mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
 
         if (t >= 1.f - EPSILON) {
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 718076b..64570a8 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -5,6 +5,8 @@
 adamp@google.com
 aurimas@google.com
 siyamed@google.com
+mount@google.com
+njawad@google.com
 
 per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2b73923..0cedcea 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2196,7 +2196,7 @@
                 int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
                 if (recycledViewIndex >= 0) {
                     View child = target.getChildAt(recycledViewIndex);
-                    if (getViewLayoutId(child) == rvToApply.getLayoutId()) {
+                    if (rvToApply.canRecycleView(child)) {
                         if (nextChild < recycledViewIndex) {
                             target.removeViews(nextChild, recycledViewIndex - nextChild);
                         }
@@ -2254,7 +2254,7 @@
                     // application are placed before.
                     ViewTree recycled = target.mChildren.get(recycledViewIndex);
                     // We can only recycle the view if the layout id is the same.
-                    if (getViewLayoutId(recycled.mRoot) == rvToApply.getLayoutId()) {
+                    if (rvToApply.canRecycleView(recycled.mRoot)) {
                         if (recycledViewIndex > nextChild) {
                             target.removeChildren(nextChild, recycledViewIndex - nextChild);
                         }
@@ -3726,7 +3726,8 @@
      *
      * The {@code stableId} will be used to identify a potential view to recycled when the remote
      * view is inflated. Views can be re-used if inserted in the same order, potentially with
-     * some views appearing / disappearing.
+     * some views appearing / disappearing. To be recycled the view must not change the layout
+     * used to inflate it or its view id (see {@link RemoteViews#setViewId}).
      *
      * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
      * are not reset, so what was applied in previous round will have an effect. As a view may be
@@ -5116,6 +5117,7 @@
         View v = inflater.inflate(rv.getLayoutId(), parent, false);
         if (mViewId != View.NO_ID) {
             v.setId(mViewId);
+            v.setTagInternal(R.id.remote_views_override_id, mViewId);
         }
         v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
         return v;
@@ -5335,6 +5337,13 @@
         reapply(context, v, handler, size, colorResources, true);
     }
 
+    /** @hide */
+    public boolean canRecycleView(View v) {
+        Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id);
+        int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag;
+        return (Integer) v.getTag(R.id.widget_frame) == getLayoutId() && mViewId == overrideId;
+    }
+
     // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
     // should set it to false.
     private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
@@ -5347,7 +5356,7 @@
         // (orientation or size), we throw an exception, since the layouts may be completely
         // unrelated.
         if (hasMultipleLayouts()) {
-            if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
+            if (!rvToApply.canRecycleView(v)) {
                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                         " that does not share the same root layout id.");
             }
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index c2e1426..7baa53b 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.accessibility.util;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
@@ -28,6 +31,10 @@
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
+import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
 
 import android.content.ComponentName;
 import android.view.accessibility.AccessibilityManager;
@@ -113,6 +120,19 @@
                 UNKNOWN_STATUS);
     }
 
+    /**
+     * Logs the magnification activated mode and its duration of the usage.
+     * Calls this when the magnification is disabled.
+     *
+     * @param mode The activated magnification mode.
+     * @param duration The duration in milliseconds during the magnification is activated.
+     */
+    public static void logMagnificationUsageState(int mode, long duration) {
+        FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED,
+                convertToLoggingMagnificationMode(mode),
+                duration);
+    }
+
     private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) {
         switch (shortcutType) {
             case ACCESSIBILITY_BUTTON:
@@ -127,4 +147,18 @@
         return enabled ? ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED
                 : ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED;
     }
+
+    private static int convertToLoggingMagnificationMode(int mode) {
+        switch (mode) {
+            case ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
+                return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
+            case ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
+                return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
+            case ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
+                return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
+
+            default:
+                return MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 11466f4..33b55ac 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -169,7 +169,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 194;
+    static final int VERSION = 195;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1009,8 +1009,12 @@
     protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats;
     /** Last known screen state. Needed for apportioning display energy. */
     int mScreenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
+    /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */
+    @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
     /** Cpu Power calculator for attributing measured cpu charge consumption to uids */
     @Nullable CpuPowerCalculator mCpuPowerCalculator = null;
+    /** Wifi Power calculator for attributing measured wifi charge consumption to uids */
+    @Nullable WifiPowerCalculator mWifiPowerCalculator = null;
 
     /**
      * These provide time bases that discount the time the device is plugged
@@ -6967,6 +6971,16 @@
     }
 
     @Override
+    public long getBluetoothMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
+    }
+
+    @Override
+    public long getCpuMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+    }
+
+    @Override
     public long getScreenOnMeasuredBatteryConsumptionUC() {
         return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
     }
@@ -6977,8 +6991,8 @@
     }
 
     @Override
-    public long getCpuMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+    public long getWifiMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
     }
 
     /**
@@ -7815,6 +7829,26 @@
             return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
         }
 
+        @Override
+        public long getBluetoothMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
+        }
+
+        @Override
+        public long getCpuMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+        }
+
+        @Override
+        public long getScreenOnMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
+        }
+
+        @Override
+        public long getWifiMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
+        }
+
         /**
          * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
          * since last marked. Also sets the mark time for both these timers.
@@ -8482,16 +8516,6 @@
             }
         }
 
-        @Override
-        public long getScreenOnMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
-        }
-
-        @Override
-        public long getCpuMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
-        }
-
         void initNetworkActivityLocked() {
             detachIfNotNull(mNetworkByteActivityCounters);
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -11437,7 +11461,7 @@
      * @param info The energy information from the WiFi controller.
      */
     public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
-            long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
         }
@@ -11459,9 +11483,21 @@
                 if (delta != null) {
                     mNetworkStatsPool.release(delta);
                 }
+                if (mIgnoreNextExternalStats) {
+                    // TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the
+                    //  global one) here like we do for display. But I'm not sure it's worth the
+                    //  complicated code for a codepath that shouldn't ever actually happen in real
+                    //  life.
+                }
                 return;
             }
 
+            final ArrayMap<Uid, Double> uidEstimatedConsumptionMah =
+                    (mGlobalMeasuredEnergyStats != null
+                            && mWifiPowerCalculator != null && consumedChargeUC > 0) ?
+                            new ArrayMap<>() : null;
+            double totalEstimatedConsumptionMah = 0;
+
             SparseLongArray rxPackets = new SparseLongArray();
             SparseLongArray txPackets = new SparseLongArray();
             long totalTxPackets = 0;
@@ -11496,6 +11532,7 @@
                         mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
                                 entry.rxPackets);
 
+                        // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
                         rxPackets.put(u.getUid(), entry.rxPackets);
 
                         // Sum the total number of packets so that the Rx Power can
@@ -11515,12 +11552,42 @@
                         mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
                                 entry.txPackets);
 
+                        // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum?
                         txPackets.put(u.getUid(), entry.txPackets);
 
                         // Sum the total number of packets so that the Tx Power can
                         // be evenly distributed amongst the apps.
                         totalTxPackets += entry.txPackets;
                     }
+
+                    // Calculate consumed energy for this uid. Only do so if WifiReporting isn't
+                    // enabled (if it is, we'll do it later instead using info).
+                    if (uidEstimatedConsumptionMah != null && info == null && !mHasWifiReporting) {
+                        final long uidRunningMs = u.mWifiRunningTimer
+                                .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+                        if (uidRunningMs > 0) u.mWifiRunningTimer.setMark(elapsedRealtimeMs);
+
+                        final long uidScanMs = u.mWifiScanTimer
+                                .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+                        if (uidScanMs > 0) u.mWifiScanTimer.setMark(elapsedRealtimeMs);
+
+                        long uidBatchScanMs = 0;
+                        for (int bn = 0; bn < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bn++) {
+                            if (u.mWifiBatchedScanTimer[bn] != null) {
+                                long bnMs = u.mWifiBatchedScanTimer[bn]
+                                        .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+                                if (bnMs > 0) {
+                                    u.mWifiBatchedScanTimer[bn].setMark(elapsedRealtimeMs);
+                                }
+                                uidBatchScanMs += bnMs;
+                            }
+                        }
+
+                        addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+                                mWifiPowerCalculator.calcPowerWithoutControllerDataMah(
+                                        entry.rxPackets, entry.txPackets,
+                                        uidRunningMs, uidScanMs, uidBatchScanMs));
+                    }
                 }
                 mNetworkStatsPool.release(delta);
                 delta = null;
@@ -11581,15 +11648,14 @@
                 for (int i = 0; i < uidStatsSize; i++) {
                     final Uid uid = mUidStats.valueAt(i);
 
-                    long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
+                    final long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
                             elapsedRealtimeMs * 1000) / 1000;
+                    long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final
+                    long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final
                     if (scanTimeSinceMarkMs > 0) {
                         // Set the new mark so that next time we get new data since this point.
                         uid.mWifiScanTimer.setMark(elapsedRealtimeMs);
 
-                        long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs;
-                        long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs;
-
                         // Our total scan time is more than the reported Tx/Rx time.
                         // This is possible because the cost of a scan is approximate.
                         // Let's normalize the result so that we evenly blame each app
@@ -11623,6 +11689,7 @@
 
                     // Distribute evenly the power consumed while Idle to each app holding a WiFi
                     // lock.
+                    long myIdleTimeMs = 0;
                     final long wifiLockTimeSinceMarkMs =
                             uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
                                     elapsedRealtimeMs * 1000) / 1000;
@@ -11630,8 +11697,7 @@
                         // Set the new mark so that next time we get new data since this point.
                         uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs);
 
-                        final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs)
-                                / totalWifiLockTimeMs;
+                        myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) / totalWifiLockTimeMs;
                         if (DEBUG_ENERGY) {
                             Slog.d(TAG, "  IdleTime for UID " + uid.getUid() + ": "
                                     + myIdleTimeMs + " ms");
@@ -11639,6 +11705,12 @@
                         uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter()
                                 .addCountLocked(myIdleTimeMs);
                     }
+
+                    if (uidEstimatedConsumptionMah != null) {
+                        double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah(
+                                scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs);
+                        addDoubleToUidMap(uidEstimatedConsumptionMah, uid, uidEstMah);
+                    }
                 }
 
                 if (DEBUG_ENERGY) {
@@ -11658,6 +11730,11 @@
                     }
                     uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0]
                             .addCountLocked(myTxTimeMs);
+                    if (uidEstimatedConsumptionMah != null) {
+                        addDoubleToUidMap(uidEstimatedConsumptionMah, uid,
+                                mWifiPowerCalculator.calcPowerFromControllerDataMah(
+                                        0, myTxTimeMs, 0));
+                    }
                 }
 
                 // Distribute the remaining Rx power appropriately between all apps that received
@@ -11672,6 +11749,11 @@
                     }
                     uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter()
                             .addCountLocked(myRxTimeMs);
+                    if (uidEstimatedConsumptionMah != null) {
+                        addDoubleToUidMap(uidEstimatedConsumptionMah, uid,
+                                mWifiPowerCalculator.calcPowerFromControllerDataMah(
+                                        myRxTimeMs, 0, 0));
+                    }
                 }
 
                 // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
@@ -11690,10 +11772,11 @@
                 // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
                 final double opVolt = mPowerProfile.getAveragePower(
                         PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+                double controllerMaMs = 0;
                 if (opVolt != 0) {
                     // We store the power drain as mAms.
-                    mWifiActivity.getPowerCounter().addCountLocked(
-                            (long) (info.getControllerEnergyUsedMicroJoules() / opVolt));
+                    controllerMaMs = info.getControllerEnergyUsedMicroJoules() / opVolt;
+                    mWifiActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
                 }
                 // Converting uWs to mAms.
                 // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
@@ -11705,6 +11788,29 @@
                         (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
                 addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
                 mTmpRailStats.resetWifiTotalEnergyUsed();
+
+                if (uidEstimatedConsumptionMah != null) {
+                    totalEstimatedConsumptionMah = Math.max(controllerMaMs / MILLISECONDS_IN_HOUR,
+                            mWifiPowerCalculator.calcPowerFromControllerDataMah(
+                                    rxTimeMs, txTimeMs, idleTimeMs));
+                }
+            }
+
+            // Update the MeasuredEnergyStats information.
+            if (uidEstimatedConsumptionMah != null) {
+                mGlobalMeasuredEnergyStats.updateStandardBucket(
+                        MeasuredEnergyStats.POWER_BUCKET_WIFI, consumedChargeUC);
+
+                // Now calculate the consumption for each uid, according to its proportional usage.
+                if (!mHasWifiReporting) {
+                    final long globalTimeMs = mGlobalWifiRunningTimer
+                            .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000;
+                    mGlobalWifiRunningTimer.setMark(elapsedRealtimeMs);
+                    totalEstimatedConsumptionMah = mWifiPowerCalculator
+                            .calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
+                }
+                distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
+                        consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah);
             }
         }
     }
@@ -11948,7 +12054,7 @@
      * @param info The energy information from the bluetooth controller.
      */
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
-            long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating bluetooth stats: " + info);
         }
@@ -11980,6 +12086,11 @@
             Slog.d(TAG, "  Idle Time:  " + idleTimeMs + " ms");
         }
 
+        final ArrayMap<Uid, Double> uidEstimatedConsumptionMah =
+                (mGlobalMeasuredEnergyStats != null
+                        && mBluetoothPowerCalculator != null && consumedChargeUC > 0) ?
+                        new ArrayMap<>() : null;
+
         long totalScanTimeMs = 0;
 
         final int uidCount = mUidStats.size();
@@ -12038,6 +12149,12 @@
                 counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs);
                 counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs);
 
+                if (uidEstimatedConsumptionMah != null) {
+                    addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+                            mBluetoothPowerCalculator.calculatePowerMah(
+                                    scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0));
+                }
+
                 leftOverRxTimeMs -= scanTimeRxSinceMarkMs;
                 leftOverTxTimeMs -= scanTimeTxSinceMarkMs;
             }
@@ -12098,6 +12215,11 @@
                         Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
                     }
                     counter.getRxTimeCounter().addCountLocked(timeRxMs);
+
+                    if (uidEstimatedConsumptionMah != null) {
+                        addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+                                mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0));
+                    }
                 }
 
                 if (totalTxBytes > 0 && txBytes > 0) {
@@ -12106,6 +12228,11 @@
                         Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
                     }
                     counter.getTxTimeCounters()[0].addCountLocked(timeTxMs);
+
+                    if (uidEstimatedConsumptionMah != null) {
+                        addDoubleToUidMap(uidEstimatedConsumptionMah, u,
+                                mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0));
+                    }
                 }
             }
         }
@@ -12117,12 +12244,26 @@
         // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
         final double opVolt = mPowerProfile.getAveragePower(
                 PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+        double controllerMaMs = 0;
         if (opVolt != 0) {
+            controllerMaMs = (info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
+                    / opVolt;
             // We store the power drain as mAms.
-            mBluetoothActivity.getPowerCounter().addCountLocked(
-                    (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy)
-                            / opVolt));
+            mBluetoothActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
         }
+
+        // Update the MeasuredEnergyStats information.
+        if (uidEstimatedConsumptionMah != null) {
+            mGlobalMeasuredEnergyStats.updateStandardBucket(
+                    MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC);
+
+            double totalEstimatedMah
+                    = mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
+            totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
+            distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+                    consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah);
+        }
+
         mLastBluetoothActivityInfo.set(info);
     }
 
@@ -12311,37 +12452,17 @@
         // If multidisplay becomes a reality, this is probably more reasonable than pooling.
 
         // On the first pass, collect total time since mark so that we can normalize power.
-        long totalFgTimeMs = 0L;
-        final ArrayMap<Uid, Long> fgTimeMsArray = new ArrayMap<>();
+        final ArrayMap<Uid, Double> fgTimeUsArray = new ArrayMap<>();
         final long elapsedRealtimeUs = elapsedRealtimeMs * 1000;
         // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids)
         final int uidStatsSize = mUidStats.size();
         for (int i = 0; i < uidStatsSize; i++) {
             final Uid uid = mUidStats.valueAt(i);
-            final long fgTimeMs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true) / 1000;
-            if (fgTimeMs == 0) continue;
-            fgTimeMsArray.put(uid, fgTimeMs);
-            totalFgTimeMs += fgTimeMs;
+            final long fgTimeUs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true);
+            if (fgTimeUs == 0) continue;
+            fgTimeUsArray.put(uid, (double) fgTimeUs);
         }
-        long totalDisplayChargeMC = chargeUC / 1000; // not final
-
-        // Actually assign and distribute power usage to apps based on their fg time since mark.
-        // TODO(b/175726326): Decide on 'energy' units and make sure algorithm won't overflow.
-        final long fgTimeArraySize = fgTimeMsArray.size();
-        for (int i = 0; i < fgTimeArraySize; i++) {
-            final Uid uid = fgTimeMsArray.keyAt(i);
-            final long fgTimeMs = fgTimeMsArray.valueAt(i);
-
-            // Using long division: "appEnergy = totalEnergy * appFg/totalFg + 0.5" with rounding
-            final long appDisplayChargeMC =
-                    (totalDisplayChargeMC * fgTimeMs + (totalFgTimeMs / 2))
-                    / totalFgTimeMs;
-            uid.addChargeToStandardBucketLocked(appDisplayChargeMC * 1000, powerBucket);
-
-            // To mitigate round-off errors, remove this app from numerator & denominator totals
-            totalDisplayChargeMC -= appDisplayChargeMC;
-            totalFgTimeMs -= fgTimeMs;
-        }
+        distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0);
     }
 
     /**
@@ -12389,6 +12510,54 @@
     }
 
     /**
+     * Attributes energy (for the given bucket) to each uid according to the following formula:
+     *     blamedEnergy[uid] = totalEnergy * ratioNumerators[uid] / ratioDenominator;
+     * <p>Does nothing if ratioDenominator is 0.
+     *
+     * <p>Here, ratioDenominator = max(sumOfAllRatioNumerators, minRatioDenominator),
+     * so if given minRatioDenominator <= 0, then sumOfAllRatioNumerators will be used implicitly.
+     *
+     * <p>Note that ratioNumerators and minRatioDenominator must use the same units, but need not
+     * use the same units as totalConsumedChargeUC (which must be in microcoulombs).
+     *
+     * <p>A consequence of minRatioDenominator is that the sum over all uids might be less than
+     * totalConsumedChargeUC. This is intentional; the remainder is purposefully unnaccounted rather
+     * than incorrectly blamed on uids, and implies unknown (non-uid) sources of drain.
+     */
+    // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>.
+    private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
+            long totalConsumedChargeUC, ArrayMap<Uid, Double> ratioNumerators,
+            double minRatioDenominator) {
+
+        // If the sum of all app usage was greater than the total, use that instead:
+        double sumRatioNumerators = 0;
+        for (int i = ratioNumerators.size() - 1; i >= 0; i--) {
+            sumRatioNumerators += ratioNumerators.valueAt(i);
+        }
+        final double ratioDenominator = Math.max(sumRatioNumerators, minRatioDenominator);
+        if (ratioDenominator <= 0) return;
+
+        for (int i = ratioNumerators.size() - 1; i >= 0; i--) {
+            final Uid uid = ratioNumerators.keyAt(i);
+            final double ratioNumerator = ratioNumerators.valueAt(i);
+            final long uidActualUC
+                    = (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5);
+            uid.addChargeToStandardBucketLocked(uidActualUC, bucket);
+        }
+    }
+
+    /** Adds the summand to the value stored in uidMap for the given uid. */
+    // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>.
+    private static void addDoubleToUidMap(ArrayMap<Uid, Double> uidMap, Uid uid, double summand) {
+        if (uidMap == null) return;
+        final Double oldVal = uidMap.get(uid);
+        if (oldVal != null) {
+            summand += oldVal;
+        }
+        uidMap.put(uid, summand);
+    }
+
+    /**
      * Read and record Rail Energy data.
      */
     public void updateRailStatsLocked() {
@@ -14222,15 +14391,20 @@
             if (mGlobalMeasuredEnergyStats == null) {
                 mGlobalMeasuredEnergyStats =
                         new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
-                return;
             } else {
                 supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
                         supportedStandardBuckets, numCustomBuckets);
             }
 
+            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) {
+                mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
+            }
             if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) {
                 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
             }
+            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
+                mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
+            }
         }
 
         if (supportedBucketMismatch) {
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 7d42de4..db14034 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -15,8 +15,11 @@
  */
 package com.android.internal.os;
 
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
+import android.os.BatteryStats.ControllerActivityCounter;
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
@@ -65,17 +68,19 @@
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
-            calculateApp(app, total);
+            calculateApp(app, total, query);
             if (app.getUid() == Process.BLUETOOTH_UID) {
                 app.excludeFromBatteryUsageStats();
                 systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
             }
         }
 
-        final BatteryStats.ControllerActivityCounter activityCounter =
+        final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ?
+                POWER_DATA_UNAVAILABLE : batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
+        final ControllerActivityCounter activityCounter =
                 batteryStats.getBluetoothControllerActivity();
         final long systemDurationMs = calculateDuration(activityCounter);
-        final double systemPowerMah = calculatePower(activityCounter);
+        final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter);
 
         // Subtract what the apps used, but clamp to 0.
         final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
@@ -91,11 +96,16 @@
                         systemComponentPowerMah);
     }
 
-    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total) {
-        final BatteryStats.ControllerActivityCounter activityCounter =
+    private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
+            BatteryUsageStatsQuery query) {
+
+        final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ?
+                POWER_DATA_UNAVAILABLE :
+                app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
+        final ControllerActivityCounter activityCounter =
                 app.getBatteryStatsUid().getBluetoothControllerActivity();
         final long durationMs = calculateDuration(activityCounter);
-        final double powerMah = calculatePower(activityCounter);
+        final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter);
 
         app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah);
@@ -121,10 +131,11 @@
         }
 
         BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
-        final BatteryStats.ControllerActivityCounter activityCounter =
+        final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
+        final ControllerActivityCounter activityCounter =
                 batteryStats.getBluetoothControllerActivity();
-        final double systemPowerMah = calculatePower(activityCounter);
         final long systemDurationMs = calculateDuration(activityCounter);
+        final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter);
 
         // Subtract what the apps used, but clamp to 0.
         final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
@@ -152,10 +163,11 @@
 
     private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
             PowerAndDuration total) {
-        final BatteryStats.ControllerActivityCounter activityCounter =
-                u.getBluetoothControllerActivity();
+
+        final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
+        final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
         final long durationMs = calculateDuration(activityCounter);
-        final double powerMah = calculatePower(activityCounter);
+        final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter);
 
         app.bluetoothRunningTimeMs = durationMs;
         app.bluetoothPowerMah = powerMah;
@@ -166,7 +178,7 @@
         total.powerMah += powerMah;
     }
 
-    private long calculateDuration(BatteryStats.ControllerActivityCounter counter) {
+    private long calculateDuration(ControllerActivityCounter counter) {
         if (counter == null) {
             return 0;
         }
@@ -176,7 +188,11 @@
                 + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
     }
 
-    private double calculatePower(BatteryStats.ControllerActivityCounter counter) {
+    /** Returns bluetooth power usage based on the best data available. */
+    private double calculatePowerMah(long measuredChargeUC, ControllerActivityCounter counter) {
+        if (measuredChargeUC != POWER_DATA_UNAVAILABLE) {
+            return uCtoMah(measuredChargeUC);
+        }
         if (counter == null) {
             return 0;
         }
@@ -195,6 +211,11 @@
                 counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
         final long txTimeMs =
                 counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+        return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
+    }
+
+    /** Returns estimated bluetooth power usage based on usage times. */
+    public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
         return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
                 / (1000 * 60 * 60);
     }
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 791e9ad..14cdb08 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -1,7 +1,11 @@
 {
   "presubmit": [
     {
-      "file_patterns": ["Battery[^/]*\\.java"],
+      "file_patterns": [
+        "Battery[^/]*\\.java",
+        "Kernel[^/]*\\.java",
+        "[^/]*Power[^/]*\\.java"
+      ],
       "name": "FrameworksCoreTests",
       "options": [
         { "include-filter": "com.android.internal.os.BatteryStatsTests" },
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 98f613f..b6bfde7 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -15,6 +15,8 @@
  */
 package com.android.internal.os;
 
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
 import android.os.BatteryUsageStats;
@@ -79,11 +81,6 @@
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
 
-        // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
-        // so always check this field.
-        final boolean hasWifiPowerReporting =
-                mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
-
         final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
                 builder.getOrCreateSystemBatteryConsumerBuilder(
                         SystemBatteryConsumer.DRAIN_TYPE_WIFI);
@@ -97,7 +94,8 @@
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
             calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs,
                     BatteryStats.STATS_SINCE_CHARGED,
-                    hasWifiPowerReporting);
+                    batteryStats.hasWifiActivityReporting(),
+                    query.shouldForceUsePowerProfileModel());
 
             totalAppDurationMs += powerDurationAndTraffic.durationMs;
             totalAppPowerMah += powerDurationAndTraffic.powerMah;
@@ -115,7 +113,9 @@
 
         calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED,
-                hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+                batteryStats.hasWifiActivityReporting(),
+                query.shouldForceUsePowerProfileModel(),
+                totalAppDurationMs, totalAppPowerMah);
 
         systemBatteryConsumerBuilder
                 .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
@@ -135,11 +135,6 @@
     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
 
-        // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
-        // so always check this field.
-        final boolean hasWifiPowerReporting =
-                mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
-
         final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
 
         long totalAppDurationMs = 0;
@@ -149,7 +144,7 @@
             final BatterySipper app = sippers.get(i);
             if (app.drainType == BatterySipper.DrainType.APP) {
                 calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType,
-                        hasWifiPowerReporting);
+                        batteryStats.hasWifiActivityReporting(), /* force use power model*/ false);
 
                 totalAppDurationMs += powerDurationAndTraffic.durationMs;
                 totalAppPowerMah += powerDurationAndTraffic.powerMah;
@@ -169,7 +164,8 @@
         }
 
         calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType,
-                hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+                batteryStats.hasWifiActivityReporting(), /* force use power model*/ false,
+                totalAppDurationMs, totalAppPowerMah);
 
         bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
         bs.wifiPowerMah += powerDurationAndTraffic.powerMah;
@@ -180,8 +176,9 @@
     }
 
     private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u,
-            long rawRealtimeUs,
-            int statsType, boolean hasWifiPowerReporting) {
+            long rawRealtimeUs, int statsType,
+            boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel) {
+
         powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
                 BatteryStats.NETWORK_WIFI_RX_DATA,
                 statsType);
@@ -195,7 +192,14 @@
                 BatteryStats.NETWORK_WIFI_TX_DATA,
                 statsType);
 
-        if (hasWifiPowerReporting) {
+        final long measuredChargeUC = u.getWifiMeasuredBatteryConsumptionUC();
+        final boolean isMeasuredPowerAvailable
+                = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE;
+        if (isMeasuredPowerAvailable) {
+            powerDurationAndTraffic.powerMah = uCtoMah(measuredChargeUC);
+        }
+
+        if (hasWifiActivityReporting && mHasWifiPowerController) {
             final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
             if (counter != null) {
                 final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
@@ -203,9 +207,10 @@
                 final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
 
                 powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
-                powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime)
-                        + mTxPowerEstimator.calculatePower(txTime)
-                        + mRxPowerEstimator.calculatePower(rxTime);
+                if (!isMeasuredPowerAvailable) {
+                    powerDurationAndTraffic.powerMah
+                            = calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
+                }
 
                 if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
                     Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
@@ -214,21 +219,20 @@
                 }
             }
         } else {
-            final double wifiPacketPower = (
-                    powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets)
-                    * mWifiPowerPerPacket;
             final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
-            final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
-            long batchScanTimeMs = 0;
-            for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
-                batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
-            }
-
             powerDurationAndTraffic.durationMs = wifiRunningTime;
-            powerDurationAndTraffic.powerMah = wifiPacketPower
-                    + mPowerOnPowerEstimator.calculatePower(wifiRunningTime)
-                    + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
-                    + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs);
+
+            if (!isMeasuredPowerAvailable) {
+                final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
+                long batchTimeMs = 0;
+                for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+                    batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
+                }
+                powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah(
+                        powerDurationAndTraffic.wifiRxPackets,
+                        powerDurationAndTraffic.wifiTxPackets,
+                        wifiRunningTime, wifiScanTimeMs, batchTimeMs);
+            }
 
             if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
                 Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
@@ -238,12 +242,20 @@
     }
 
     private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
-            BatteryStats stats, long rawRealtimeUs,
-            int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs,
-            double totalAppPowerMah) {
+            BatteryStats stats, long rawRealtimeUs, int statsType,
+            boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel,
+            long totalAppDurationMs, double totalAppPowerMah) {
+
         long totalDurationMs;
-        double totalPowerMah;
-        if (hasWifiPowerReporting) {
+        double totalPowerMah = 0;
+
+        final long measuredChargeUC = stats.getWifiMeasuredBatteryConsumptionUC();
+        final boolean isMeasuredPowerAvailable
+                = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE;
+        if (isMeasuredPowerAvailable) {
+            totalPowerMah = uCtoMah(measuredChargeUC);
+        }
+        if (hasWifiActivityReporting && mHasWifiPowerController) {
             final BatteryStats.ControllerActivityCounter counter =
                     stats.getWifiControllerActivity();
 
@@ -253,17 +265,19 @@
 
             totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;
 
-            totalPowerMah =
-                    counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60);
-            if (totalPowerMah == 0) {
-                // Some controllers do not report power drain, so we can calculate it here.
-                totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs)
-                        + mTxPowerEstimator.calculatePower(txTimeMs)
-                        + mRxPowerEstimator.calculatePower(rxTimeMs);
+            if (!isMeasuredPowerAvailable) {
+                totalPowerMah = counter.getPowerCounter().getCountLocked(statsType)
+                        / (double) (1000 * 60 * 60);
+                if (totalPowerMah == 0) {
+                    // Some controllers do not report power drain, so we can calculate it here.
+                    totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs);
+                }
             }
         } else {
             totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
-            totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs);
+            if (!isMeasuredPowerAvailable) {
+                totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs);
+            }
         }
 
         powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
@@ -274,6 +288,29 @@
         }
     }
 
+    /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */
+    public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
+        return mRxPowerEstimator.calculatePower(rxTimeMs)
+                + mTxPowerEstimator.calculatePower(txTimeMs)
+                + mIdlePowerEstimator.calculatePower(idleTimeMs);
+    }
+
+    /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */
+    public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets,
+            long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) {
+        return
+                (rxPackets + txPackets) * mWifiPowerPerPacket
+                + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs)
+                + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
+                + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs);
+
+    }
+
+    /** Returns global estimated wifi power used using non-WifiControllerActivity data. */
+    public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) {
+        return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs);
+    }
+
     /**
      * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
      */
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index e3d5464..845b3e5 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -52,7 +52,9 @@
     public static final int POWER_BUCKET_SCREEN_DOZE = 1;
     public static final int POWER_BUCKET_SCREEN_OTHER = 2;
     public static final int POWER_BUCKET_CPU = 3;
-    public static final int NUMBER_STANDARD_POWER_BUCKETS = 4; // Buckets above this are custom.
+    public static final int POWER_BUCKET_WIFI = 4;
+    public static final int POWER_BUCKET_BLUETOOTH = 5;
+    public static final int NUMBER_STANDARD_POWER_BUCKETS = 6; // Buckets above this are custom.
 
     @IntDef(prefix = {"POWER_BUCKET_"}, value = {
             POWER_BUCKET_UNKNOWN,
@@ -60,6 +62,8 @@
             POWER_BUCKET_SCREEN_DOZE,
             POWER_BUCKET_SCREEN_OTHER,
             POWER_BUCKET_CPU,
+            POWER_BUCKET_WIFI,
+            POWER_BUCKET_BLUETOOTH,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StandardPowerBucket {
diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java
deleted file mode 100644
index d67bd7a..0000000
--- a/core/java/com/android/internal/util/LocationPermissionChecker.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.net.NetworkStack;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-
-/**
- * The methods used for location permission and location mode checking.
- *
- * @hide
- */
-public class LocationPermissionChecker {
-
-    private static final String TAG = "LocationPermissionChecker";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"LOCATION_PERMISSION_CHECK_STATUS_"}, value = {
-        SUCCEEDED,
-        ERROR_LOCATION_MODE_OFF,
-        ERROR_LOCATION_PERMISSION_MISSING,
-    })
-    public @interface LocationPermissionCheckStatus{}
-
-    // The location permission check succeeded.
-    public static final int SUCCEEDED = 0;
-    // The location mode turns off for the caller.
-    public static final int ERROR_LOCATION_MODE_OFF = 1;
-    // The location permission isn't granted for the caller.
-    public static final int ERROR_LOCATION_PERMISSION_MISSING = 2;
-
-    private final Context mContext;
-    private final AppOpsManager mAppOpsManager;
-
-    public LocationPermissionChecker(Context context) {
-        mContext = context;
-        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-    }
-
-    /**
-     * Check location permission granted by the caller.
-     *
-     * This API check if the location mode enabled for the caller and the caller has
-     * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
-     *
-     * @param pkgName package name of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     *
-     * @return {@code true} returns if the caller has location permission and the location mode is
-     *         enabled.
-     */
-    public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
-            int uid, @Nullable String message) {
-        return checkLocationPermissionInternal(pkgName, featureId, uid, message) == SUCCEEDED;
-    }
-
-    /**
-     * Check location permission granted by the caller.
-     *
-     * This API check if the location mode enabled for the caller and the caller has
-     * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
-     * Compared with {@link #checkLocationPermission(String, String, int, String)}, this API returns
-     * the detail information about the checking result, including the reason why it's failed and
-     * logs the error for the caller.
-     *
-     * @param pkgName package name of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     *
-     * @return {@link LocationPermissionCheckStatus} the result of the location permission check.
-     */
-    public @LocationPermissionCheckStatus int checkLocationPermissionWithDetailInfo(
-            String pkgName, @Nullable String featureId, int uid, @Nullable String message) {
-        final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
-        switch (result) {
-            case ERROR_LOCATION_MODE_OFF:
-                Log.e(TAG, "Location mode is disabled for the device");
-                break;
-            case ERROR_LOCATION_PERMISSION_MISSING:
-                Log.e(TAG, "UID " + uid + " has no location permission");
-                break;
-        }
-        return result;
-    }
-
-    /**
-     * Enforce the caller has location permission.
-     *
-     * This API determines if the location mode enabled for the caller and the caller has
-     * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
-     * SecurityException is thrown if the caller has no permission or the location mode is disabled.
-     *
-     * @param pkgName package name of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     */
-    public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
-            @Nullable String message) throws SecurityException {
-        final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
-
-        switch (result) {
-            case ERROR_LOCATION_MODE_OFF:
-                throw new SecurityException("Location mode is disabled for the device");
-            case ERROR_LOCATION_PERMISSION_MISSING:
-                throw new SecurityException("UID " + uid + " has no location permission");
-        }
-    }
-
-    private int checkLocationPermissionInternal(String pkgName, @Nullable String featureId,
-            int uid, @Nullable String message) {
-        checkPackage(uid, pkgName);
-
-        // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK
-        // are granted a bypass.
-        if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
-                || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
-            return SUCCEEDED;
-        }
-
-        // Location mode must be enabled
-        if (!isLocationModeEnabled()) {
-            return ERROR_LOCATION_MODE_OFF;
-        }
-
-        // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
-        // location information.
-        if (!checkCallersLocationPermission(pkgName, featureId, uid,
-                true /* coarseForTargetSdkLessThanQ */, message)) {
-            return ERROR_LOCATION_PERMISSION_MISSING;
-        }
-        return SUCCEEDED;
-    }
-
-    /**
-     * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
-     * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
-     * and a corresponding app op is allowed for this package and uid.
-     *
-     * @param pkgName PackageName of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
-     *                                    else (false or targetSDK >= Q) then will check for FINE
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     */
-    public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
-            int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
-
-        boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
-
-        String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
-        if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
-            // Having FINE permission implies having COARSE permission (but not the reverse)
-            permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
-        }
-        if (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) {
-            return false;
-        }
-
-        // Always checking FINE - even if will not enforce. This will record the request for FINE
-        // so that a location request by the app is surfaced to the user.
-        boolean isFineLocationAllowed = noteAppOpAllowed(
-                AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
-        if (isFineLocationAllowed) {
-            return true;
-        }
-        if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
-            return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
-                    message);
-        }
-        return false;
-    }
-
-    /**
-     * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
-     */
-    public boolean isLocationModeEnabled() {
-        final LocationManager LocationManager =
-                (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
-        try {
-            return LocationManager.isLocationEnabledForUser(UserHandle.of(
-                    getCurrentUser()));
-        } catch (Exception e) {
-            Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
-            return false;
-        }
-    }
-
-    private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            if (mContext.getPackageManager().getApplicationInfoAsUser(
-                    packageName, 0,
-                    UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
-                    < versionCode) {
-                return true;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // In case of exception, assume unknown app (more strict checking)
-            // Note: This case will never happen since checkPackage is
-            // called to verify validity before checking App's version.
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return false;
-    }
-
-    private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
-            int uid, @Nullable String message) {
-        return mAppOpsManager.noteOp(op, uid, pkgName, featureId, message)
-                == AppOpsManager.MODE_ALLOWED;
-    }
-
-    private void checkPackage(int uid, String pkgName)
-            throws SecurityException {
-        if (pkgName == null) {
-            throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
-        }
-        mAppOpsManager.checkPackage(uid, pkgName);
-    }
-
-    @VisibleForTesting
-    protected int getCurrentUser() {
-        return ActivityManager.getCurrentUser();
-    }
-
-    private int getUidPermission(String permissionType, int uid) {
-        // We don't care about pid, pass in -1
-        return mContext.checkPermission(permissionType, -1, uid);
-    }
-
-    /**
-     * Returns true if the |uid| holds NETWORK_SETTINGS permission.
-     */
-    public boolean checkNetworkSettingsPermission(int uid) {
-        return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
-     */
-    public boolean checkNetworkSetupWizardPermission(int uid) {
-        return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Returns true if the |uid| holds NETWORK_STACK permission.
-     */
-    public boolean checkNetworkStackPermission(int uid) {
-        return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
-     */
-    public boolean checkMainlineNetworkStackPermission(int uid) {
-        return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 276cad9..bbd738b 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -59,8 +59,6 @@
     public static final int BASE_TETHERING                                          = 0x00050000;
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
-    public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00080000;
-    public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
     public static final int BASE_NETWORK_FACTORY                                    = 0x00083000;
     public static final int BASE_ETHERNET                                           = 0x00084000;
     public static final int BASE_LOWPAN                                             = 0x00085000;
diff --git a/core/java/com/android/internal/view/inline/OWNERS b/core/java/com/android/internal/view/inline/OWNERS
new file mode 100644
index 0000000..edfb211
--- /dev/null
+++ b/core/java/com/android/internal/view/inline/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2b665c0..a5fbae9 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -513,17 +513,17 @@
 
     optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
         (section).type = SECTION_DUMPSYS,
-        (section).args = "power_stats --proto meter"
+        (section).args = "powerstats --proto meter"
     ];
 
     optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
         (section).type = SECTION_DUMPSYS,
-        (section).args = "power_stats --proto model"
+        (section).args = "powerstats --proto model"
     ];
 
     optional com.android.server.powerstats.PowerStatsServiceResidencyProto powerstats_residency = 3056 [
         (section).type = SECTION_DUMPSYS,
-        (section).args = "power_stats --proto residency"
+        (section).args = "powerstats --proto residency"
     ];
 
     // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 7bc4663..c3b35c8 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -252,5 +252,8 @@
   <item type="id" name="remote_views_next_child" />
 
   <!-- View tag associating a view with its stable id for potential recycling. -->
-  <item type="id" name = "remote_views_stable_id" />
+  <item type="id" name="remote_views_stable_id" />
+
+  <!-- View tag associating a view with its overridden id, to ensure valid recycling only. -->
+  <item type="id" name="remote_views_override_id" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b935788..ef5191af 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4322,4 +4322,5 @@
 
   <java-symbol type="id" name="remote_views_next_child" />
   <java-symbol type="id" name="remote_views_stable_id" />
+  <java-symbol type="id" name="remote_views_override_id" />
 </resources>
diff --git a/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml b/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml
deleted file mode 100644
index 0435c30..0000000
--- a/core/res/res/values/vendor_allowed_personal_apps_org_owned_device.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources>
-    <!-- A list of apps to be allowed in the personal profile of an organization-owned device. -->
-    <string-array translatable="false" name="vendor_allowed_personal_apps_org_owned_device">
-    </string-array>
-</resources>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index ee472880..46e2772 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -60,6 +60,7 @@
         KernelCpuUidFreqTimeReaderTest.class,
         KernelCpuUidUserSysTimeReaderTest.class,
         KernelMemoryBandwidthStatsTest.class,
+        KernelSingleProcessCpuThreadReaderTest.class,
         KernelSingleUidTimeReaderTest.class,
         KernelWakelockReaderTest.class,
         LongSamplingCounterTest.class,
@@ -69,6 +70,7 @@
         PowerProfileTest.class,
         ScreenPowerCalculatorTest.class,
         SensorPowerCalculatorTest.class,
+        SystemServerCpuThreadReaderTest.class,
         SystemServicePowerCalculatorTest.class,
         UserPowerCalculatorTest.class,
         VideoPowerCalculatorTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 8aeb761..80ab36e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -33,11 +33,15 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.power.MeasuredEnergyStats;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 import org.mockito.stubbing.Answer;
 
+import java.util.Arrays;
+
 public class BatteryUsageStatsRule implements TestRule {
     private final PowerProfile mPowerProfile;
     private final MockClocks mMockClocks = new MockClocks();
@@ -98,6 +102,16 @@
         return this;
     }
 
+    /** Call only after setting the power profile information. */
+    public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(int numCustom) {
+        final boolean[] supportedStandardBuckets =
+                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+        Arrays.fill(supportedStandardBuckets, true);
+        mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, numCustom);
+        mBatteryStats.informThatAllExternalStatsAreFlushed();
+        return this;
+    }
+
     public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
         mScreenOn = screenOn;
         return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 23ea508..33b8aed 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -35,7 +35,6 @@
 import java.util.List;
 
 @SmallTest
-@SkipPresubmit("b/180015146")
 @RunWith(AndroidJUnit4.class)
 public class BatteryUsageStatsTest {
 
@@ -102,7 +101,7 @@
     }
 
     public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
-        assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100);
+        assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(21500);
         assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
         assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
         assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
@@ -128,7 +127,7 @@
                         BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
                 assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
                         BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
-                assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710);
+                assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200);
             } else {
                 fail("Unexpected UID " + uidBatteryConsumer.getUid());
             }
@@ -146,7 +145,7 @@
                         BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
                 assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
                         BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
-                assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510);
+                assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300);
             } else {
                 fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
             }
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index f6aa08b..71d7668 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -20,6 +20,7 @@
 
 import android.annotation.Nullable;
 import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.SystemBatteryConsumer;
 
@@ -43,7 +44,6 @@
             .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0);
 
     @Test
-    @SkipPresubmit("b/180015146")
     public void testTimerBasedModel() {
         setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
                         .getOrCreateBluetoothControllerActivityLocked(),
@@ -60,11 +60,10 @@
         BluetoothPowerCalculator calculator =
                 new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
 
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
-                0.11388, 6000);
+        assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
         assertBluetoothPowerAndDuration(
                 mStatsRule.getUidBatteryConsumer(APP_UID),
                 0.24722, 15000);
@@ -74,7 +73,6 @@
     }
 
     @Test
-    @SkipPresubmit("b/180015146")
     public void testReportedPowerBasedModel() {
         setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
                         .getOrCreateBluetoothControllerActivityLocked(),
@@ -91,11 +89,10 @@
         BluetoothPowerCalculator calculator =
                 new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
 
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
-                0.1, 6000);
+        assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
         assertBluetoothPowerAndDuration(
                 mStatsRule.getUidBatteryConsumer(APP_UID),
                 0.2, 15000);
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 10ff3a4..496415a 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -101,7 +101,6 @@
     }
 
     @Test
-    @SkipPresubmit("b/180015146")
     public void testTimerBasedModel() {
         when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
index 0c91b29..f011117 100644
--- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -42,9 +42,12 @@
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
 
     @Test
-    @SkipPresubmit("b/180015146")
     public void testMeasuredEnergyCopiedIntoBatteryConsumers() {
         final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        // For side-effect of creating a BatteryStats.Uid
+        batteryStats.getUidStatsLocked(APP_UID);
+
         SparseLongArray uidEnergies = new SparseLongArray();
         uidEnergies.put(APP_UID, 30_000_000);
         batteryStats.updateCustomMeasuredEnergyStatsLocked(0, 100_000_000, uidEnergies);
@@ -60,18 +63,18 @@
         UidBatteryConsumer uid = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uid.getConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
-                .isWithin(PRECISION).of(2.252252);
+                .isWithin(PRECISION).of(8.333333);
         assertThat(uid.getConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
-                .isWithin(PRECISION).of(9.009009);
+                .isWithin(PRECISION).of(33.33333);
 
         SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(
                 SystemBatteryConsumer.DRAIN_TYPE_CUSTOM);
         assertThat(systemConsumer.getConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
-                .isWithin(PRECISION).of(7.5075075);
+                .isWithin(PRECISION).of(27.77777);
         assertThat(systemConsumer.getConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
-                .isWithin(PRECISION).of(15.015015);
+                .isWithin(PRECISION).of(55.55555);
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index a47c4d8..26adbe9 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -46,6 +46,7 @@
         initTimersAndCounters();
 
         setExternalStatsSyncLocked(new DummyExternalStatsSync());
+        informThatAllExternalStatsAreFlushed();
 
         final boolean[] supportedStandardBuckets =
                 new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index 6edbbb0..58e2513 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -78,7 +78,6 @@
     }
 
     @Test
-    @SkipPresubmit("b/180015146")
     public void testPowerProfileBasedModel() {
         when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
 
@@ -154,7 +153,7 @@
         }
 
         @Override
-        public void readDelta(@Nullable Callback<long[]> cb) {
+        public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) {
             if (cb != null) {
                 cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
             }
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index e100545..2e23dc8 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -17,11 +17,14 @@
 package com.android.internal.os;
 
 
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.SystemBatteryConsumer;
 import android.os.UidBatteryConsumer;
@@ -50,10 +53,11 @@
             .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0)
             .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0)
             .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0)
-            .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0);
+            .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0)
+            .initMeasuredEnergyStatsLocked(0);
 
-    @Test
-    public void testPowerControllerBasedModel() {
+    /** Sets up a batterystats object with pre-populated network values. */
+    private BatteryStatsImpl setupTestNetworkNumbers() {
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
         batteryStats.noteNetworkInterfaceForTransports("wifi",
@@ -64,13 +68,25 @@
                 .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
         mStatsRule.setNetworkStats(networkStats);
 
-        WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000,
-                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+        return batteryStats;
+    }
 
-        batteryStats.updateWifiState(energyInfo, 1000, 1000);
+    /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */
+    private WifiActivityEnergyInfo setupPowerControllerBasedModelEnergyNumbersInfo() {
+        return new WifiActivityEnergyInfo(10000,
+                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+    }
+
+    @Test
+    public void testPowerControllerBasedModel_nonMeasured() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+        final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
+
+        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
         UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
@@ -87,30 +103,54 @@
     }
 
     @Test
-    public void testTimerBasedModel() {
-        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+    public void testPowerControllerBasedModel_measured() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
+        final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
 
-        batteryStats.noteNetworkInterfaceForTransports("wifi",
-                new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+        batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000);
 
-        NetworkStats networkStats = new NetworkStats(10000, 1)
-                .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
-                .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
-        mStatsRule.setNetworkStats(networkStats);
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(calculator);
 
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(1423);
+        /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
+
+        SystemBatteryConsumer systemConsumer =
+                mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+        assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(5577);
+        /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+        assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.645200 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
+    }
+
+    /** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */
+    private BatteryStatsImpl setupTimerBasedModelTestNumbers() {
+        final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000);
         batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000);
         batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000);
         batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000);
         batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222);
         batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444);
+        return batteryStats;
+    }
+
+    @Test
+    public void testTimerBasedModel_nonMeasured() {
+        final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers();
 
         // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
         // on the packet counts.
-        batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000);
+        batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
         UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
@@ -125,4 +165,31 @@
         assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.8759216);
     }
+
+    @Test
+    public void testTimerBasedModel_measured() {
+        final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers();
+
+        // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
+        // on the packet counts.
+        batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000);
+
+        WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+        assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(1000);
+        /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+        assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
+
+        SystemBatteryConsumer systemConsumer =
+                mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+        assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+                .isEqualTo(2222);
+        /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
+        assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+                .isWithin(PRECISION).of(0.8759216 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
+    }
 }
diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
deleted file mode 100644
index 7175f56..0000000
--- a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.util;
-
-import static android.Manifest.permission.NETWORK_SETTINGS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.HashMap;
-
-/** Unit tests for {@link LocationPermissionChecker}. */
-public class LocationPermissionCheckerTest {
-
-    public static final String TAG = "ConnectivityUtilTest";
-
-    // Mock objects for testing
-    @Mock private Context mMockContext;
-    @Mock private PackageManager mMockPkgMgr;
-    @Mock private ApplicationInfo mMockApplInfo;
-    @Mock private AppOpsManager mMockAppOps;
-    @Mock private UserManager mMockUserManager;
-    @Mock private LocationManager mLocationManager;
-
-    private static final String TEST_PKG_NAME = "com.google.somePackage";
-    private static final String TEST_FEATURE_ID = "com.google.someFeature";
-    private static final int MANAGED_PROFILE_UID = 1100000;
-    private static final int OTHER_USER_UID = 1200000;
-
-    private final String mInteractAcrossUsersFullPermission =
-            "android.permission.INTERACT_ACROSS_USERS_FULL";
-    private final String mManifestStringCoarse =
-            Manifest.permission.ACCESS_COARSE_LOCATION;
-    private final String mManifestStringFine =
-            Manifest.permission.ACCESS_FINE_LOCATION;
-
-    // Test variables
-    private int mWifiScanAllowApps;
-    private int mUid;
-    private int mCoarseLocationPermission;
-    private int mAllowCoarseLocationApps;
-    private int mFineLocationPermission;
-    private int mAllowFineLocationApps;
-    private int mNetworkSettingsPermission;
-    private int mCurrentUser;
-    private boolean mIsLocationEnabled;
-    private boolean mThrowSecurityException;
-    private Answer<Integer> mReturnPermission;
-    private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
-    private LocationPermissionChecker mChecker;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        initTestVars();
-    }
-
-    private void setupMocks() throws Exception {
-        when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
-                .thenReturn(mMockApplInfo);
-        when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
-        when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
-                TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
-        when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
-                eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
-                .thenReturn(mAllowCoarseLocationApps);
-        when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
-                eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
-                .thenReturn(mAllowFineLocationApps);
-        if (mThrowSecurityException) {
-            doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
-                    + " to application bound to user " + mUid))
-                    .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
-        }
-        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
-                .thenReturn(mMockAppOps);
-        when(mMockContext.getSystemService(Context.USER_SERVICE))
-                .thenReturn(mMockUserManager);
-        when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
-    }
-
-    private void setupTestCase() throws Exception {
-        setupMocks();
-        setupMockInterface();
-        mChecker = new LocationPermissionChecker(mMockContext);
-    }
-
-    private void initTestVars() {
-        mPermissionsList.clear();
-        mReturnPermission = createPermissionAnswer();
-        mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
-        mUid = OTHER_USER_UID;
-        mThrowSecurityException = true;
-        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
-        mIsLocationEnabled = false;
-        mCurrentUser = ActivityManager.getCurrentUser();
-        mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
-        mFineLocationPermission = PackageManager.PERMISSION_DENIED;
-        mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
-        mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED;
-    }
-
-    private void setupMockInterface() {
-        Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
-        doAnswer(mReturnPermission).when(mMockContext).checkPermission(
-                anyString(), anyInt(), anyInt());
-        when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
-                UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
-                .thenReturn(true);
-        when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
-                .thenReturn(mCoarseLocationPermission);
-        when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
-                .thenReturn(mFineLocationPermission);
-        when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid))
-                .thenReturn(mNetworkSettingsPermission);
-        when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
-    }
-
-    private Answer<Integer> createPermissionAnswer() {
-        return new Answer<Integer>() {
-            @Override
-            public Integer answer(InvocationOnMock invocation) {
-                int myUid = (int) invocation.getArguments()[1];
-                String myPermission = (String) invocation.getArguments()[0];
-                mPermissionsList.get(myPermission);
-                if (mPermissionsList.containsKey(myPermission)) {
-                    int uid = mPermissionsList.get(myPermission);
-                    if (myUid == uid) {
-                        return PackageManager.PERMISSION_GRANTED;
-                    }
-                }
-                return PackageManager.PERMISSION_DENIED;
-            }
-        };
-    }
-
-    @Test
-    public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
-        mIsLocationEnabled = true;
-        mThrowSecurityException = false;
-        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        mUid = mCurrentUser;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.SUCCEEDED, result);
-    }
-
-    @Test
-    public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
-        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        mIsLocationEnabled = true;
-        mThrowSecurityException = false;
-        mUid = mCurrentUser;
-        mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.SUCCEEDED, result);
-    }
-
-    @Test
-    public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
-        mThrowSecurityException = true;
-        mIsLocationEnabled = true;
-        mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        setupTestCase();
-
-        assertThrows(SecurityException.class,
-                () -> mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
-        mThrowSecurityException = false;
-        mIsLocationEnabled = true;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
-        mThrowSecurityException = false;
-        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        mIsLocationEnabled = true;
-        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
-        mUid = MANAGED_PROFILE_UID;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
-        verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
-        mThrowSecurityException = false;
-        mUid = MANAGED_PROFILE_UID;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
-        mIsLocationEnabled = false;
-
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result);
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings()
-            throws Exception {
-        mThrowSecurityException = false;
-        mIsLocationEnabled = false;
-        mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.SUCCEEDED, result);
-    }
-
-
-    private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
-        try {
-            r.run();
-            Assert.fail("Expected " + exceptionClass + " to be thrown.");
-        } catch (Exception exception) {
-            assertTrue(exceptionClass.isInstance(exception));
-        }
-    }
-}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 937f01c..a08f390 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -207,7 +207,7 @@
                     case UserState.LSKF_LOCKED:
                         return KeyStore.State.LOCKED;
                     default:
-                        throw new AssertionError(KeyStore.VALUE_CORRUPTED);
+                        throw new AssertionError(userState);
                 }
             }
             ret = mBinder.getState(userId);
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 971a53a..e58f31f 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -126,7 +126,7 @@
     SkAndroidFrameworkTraceUtil::setEnableTracing(
             base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
 
-    runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
+    runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false);
 
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index dcb79ba..42aa87b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -160,7 +160,7 @@
 /**
  * Property for whether this is running in the emulator.
  */
-#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
+#define PROPERTY_IS_EMULATOR "ro.boot.qemu"
 
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java
index 4b6e688..718977f 100644
--- a/location/java/android/location/CorrelationVector.java
+++ b/location/java/android/location/CorrelationVector.java
@@ -77,6 +77,9 @@
      * be encoded as signed 16 bit integer where 1 is represented by 32767 and -1 is represented
      * by -32768.
      *
+     * <p>The values are quantized using a 16bit integer to save on the data size since the array
+     * contains real data and it might grow.
+     *
      */
     @NonNull
     public int[] getMagnitude() {
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index 84a363d..dbf2621 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -363,10 +363,10 @@
      * <p>The bytes (or words) specified using big endian format (MSB first).
      *
      * <ul>
-     * <li>For GPS L1 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10 30-bit words. Each
-     * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with
-     * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds,
-     * respectively.</li>
+     * <li>For GPS L1 C/A, IRNSS L5 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10
+     * 30-bit words. Each word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip
+     * B31 and B32), with MSB first, for a total of 40 bytes, covering a time period of 6, 6, and
+     * 0.6 seconds, respectively.</li>
      * <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum.  These
      * bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2
      * seconds.</li>
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index f8a642a..4e8a273 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -161,6 +161,10 @@
     public static final String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
     public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
     public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
+    /** MIME type for MPEG-H Audio single stream */
+    public static final String MIMETYPE_AUDIO_MPEGH_MHA1 = "audio/mha1";
+    /** MIME type for MPEG-H Audio single stream, encapsulated in MHAS */
+    public static final String MIMETYPE_AUDIO_MPEGH_MHM1 = "audio/mhm1";
 
     /**
      * MIME type for HEIF still image data encoded in HEVC.
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 9566e1c..02fa040 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -259,7 +259,8 @@
         mClientPackageName = clientPackageName;
         mManagerCallback = new ManagerCallback();
         mHandler = new Handler(Looper.getMainLooper());
-        mSystemController = new SystemRoutingController(sManager.getSystemRoutingSession());
+        mSystemController = new SystemRoutingController(
+                ensureClientPackageNameForSystemSession(sManager.getSystemRoutingSession()));
         mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
                 sManager.getPreferredFeatures(clientPackageName), true).build();
         updateAllRoutesFromManager();
@@ -691,7 +692,8 @@
             for (RoutingSessionInfo session : sessions) {
                 RoutingController controller;
                 if (session.isSystemSession()) {
-                    mSystemController.setRoutingSessionInfo(session);
+                    mSystemController.setRoutingSessionInfo(
+                            ensureClientPackageNameForSystemSession(session));
                     controller = mSystemController;
                 } else {
                     controller = new RoutingController(session);
@@ -1019,6 +1021,23 @@
         sManager.registerCallback(Runnable::run, mManagerCallback);
     }
 
+    /**
+     * Returns a {@link RoutingSessionInfo} which has the client package name.
+     * The client package name is set only when the given sessionInfo doesn't have it.
+     * Should only used for system media routers.
+     */
+    private RoutingSessionInfo ensureClientPackageNameForSystemSession(
+            @NonNull RoutingSessionInfo sessionInfo) {
+        if (!sessionInfo.isSystemSession()
+                || !TextUtils.isEmpty(sessionInfo.getClientPackageName())) {
+            return sessionInfo;
+        }
+
+        return new RoutingSessionInfo.Builder(sessionInfo)
+                .setClientPackageName(mClientPackageName)
+                .build();
+    }
+
     private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
             RouteDiscoveryPreference discoveryRequest) {
         return routes.stream()
@@ -1942,7 +1961,8 @@
 
             RoutingController oldController;
             if (oldSession.isSystemSession()) {
-                mSystemController.setRoutingSessionInfo(oldSession);
+                mSystemController.setRoutingSessionInfo(
+                        ensureClientPackageNameForSystemSession(oldSession));
                 oldController = mSystemController;
             } else {
                 oldController = new RoutingController(oldSession);
@@ -1950,7 +1970,8 @@
 
             RoutingController newController;
             if (newSession.isSystemSession()) {
-                mSystemController.setRoutingSessionInfo(newSession);
+                mSystemController.setRoutingSessionInfo(
+                        ensureClientPackageNameForSystemSession(newSession));
                 newController = mSystemController;
             } else {
                 newController = new RoutingController(newSession);
@@ -1978,7 +1999,8 @@
 
             RoutingController controller;
             if (session.isSystemSession()) {
-                mSystemController.setRoutingSessionInfo(session);
+                mSystemController.setRoutingSessionInfo(
+                        ensureClientPackageNameForSystemSession(session));
                 controller = mSystemController;
             } else {
                 controller = new RoutingController(session);
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 49c2b39..309d71c 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -358,14 +358,14 @@
 void StreamManager::run(int32_t id)
 {
     ALOGV("%s(%d) entering", __func__, id);
-    int64_t waitTimeNs = kWaitTimeBeforeCloseNs;
+    int64_t waitTimeNs = 0;  // on thread start, mRestartStreams can be non-empty.
     std::unique_lock lock(mStreamManagerLock);
     while (!mQuit) {
-        if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty.
+        if (waitTimeNs > 0) {
             mStreamManagerCondition.wait_for(
                     lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs));
         }
-        ALOGV("%s(%d) awake", __func__, id);
+        ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs);
 
         sanityCheckQueue_l();
 
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 243e4ca..f22d4b7 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -87,6 +87,7 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method @Deprecated public static boolean isNetworkTypeValid(int);
+    method public void registerBestMatchingNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index b9bcddb..bb29647 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -11,6 +11,7 @@
     method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
     field public static final String PRIVATE_DNS_MODE_OFF = "off";
     field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
@@ -29,6 +30,11 @@
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
+  public class ParseException extends java.lang.RuntimeException {
+    ctor public ParseException(@NonNull String);
+    ctor public ParseException(@NonNull String, @NonNull Throwable);
+  }
+
   public final class TcpRepairWindow {
     ctor public TcpRepairWindow(int, int, int, int, int, int);
     field public final int maxWindow;
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index a98f14e..4dca411 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -56,7 +56,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -67,6 +67,8 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+    field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
+    field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
     field public static final int TETHERING_BLUETOOTH = 2; // 0x2
     field public static final int TETHERING_USB = 1; // 0x1
     field public static final int TETHERING_WIFI = 0; // 0x0
@@ -78,10 +80,6 @@
     field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
   }
 
-  public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
-    method public void onComplete();
-  }
-
   @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
     ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
     method @Deprecated public void onTetheringFailed();
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 6d6a554..ba5eb10 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -18,8 +18,8 @@
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
 import static android.net.NetworkRequest.Type.LISTEN;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
 import static android.net.NetworkRequest.Type.REQUEST;
-import static android.net.NetworkRequest.Type.TRACK_BEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
 import static android.net.QosCallback.QosCallbackRegistrationException;
@@ -64,6 +64,7 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -76,7 +77,6 @@
 import com.android.connectivity.aidl.INetworkAgent;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.Protocol;
 
 import libcore.net.event.NetworkEventDispatcher;
 
@@ -971,6 +971,33 @@
     }
 
     /**
+     * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+     * Specify that the traffic for this user should by follow the default rules.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
+
+    /**
+     * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+     * Specify that the traffic for this user should by default go on a network with
+     * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
+     * if no such network is available.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            PROFILE_NETWORK_PREFERENCE_DEFAULT,
+            PROFILE_NETWORK_PREFERENCE_ENTERPRISE
+    })
+    public @interface ProfileNetworkPreference {
+    }
+
+    /**
      * Specifies the preferred network type.  When the device has more
      * than one type available the preferred network type will be used.
      *
@@ -3213,10 +3240,6 @@
         }
     }
 
-    // TODO : remove this method. It is a stopgap measure to help sheperding a number
-    // of dependent changes that would conflict throughout the automerger graph. Having this
-    // temporarily helps with the process of going through with all these dependent changes across
-    // the entire tree.
     /**
      * @hide
      * Register a NetworkAgent with ConnectivityService.
@@ -3226,20 +3249,8 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_FACTORY})
     public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config) {
-        return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
-    }
-
-    /**
-     * @hide
-     * Register a NetworkAgent with ConnectivityService.
-     * @return Network corresponding to NetworkAgent.
-     */
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+            NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config,
+            int providerId) {
         try {
             return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
@@ -3539,29 +3550,28 @@
         }
     }
 
-    private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
     /** @hide */
-    public static final int CALLBACK_PRECHECK            = BASE + 1;
+    public static final int CALLBACK_PRECHECK            = 1;
     /** @hide */
-    public static final int CALLBACK_AVAILABLE           = BASE + 2;
+    public static final int CALLBACK_AVAILABLE           = 2;
     /** @hide arg1 = TTL */
-    public static final int CALLBACK_LOSING              = BASE + 3;
+    public static final int CALLBACK_LOSING              = 3;
     /** @hide */
-    public static final int CALLBACK_LOST                = BASE + 4;
+    public static final int CALLBACK_LOST                = 4;
     /** @hide */
-    public static final int CALLBACK_UNAVAIL             = BASE + 5;
+    public static final int CALLBACK_UNAVAIL             = 5;
     /** @hide */
-    public static final int CALLBACK_CAP_CHANGED         = BASE + 6;
+    public static final int CALLBACK_CAP_CHANGED         = 6;
     /** @hide */
-    public static final int CALLBACK_IP_CHANGED          = BASE + 7;
+    public static final int CALLBACK_IP_CHANGED          = 7;
     /** @hide obj = NetworkCapabilities, arg1 = seq number */
-    private static final int EXPIRE_LEGACY_REQUEST       = BASE + 8;
+    private static final int EXPIRE_LEGACY_REQUEST       = 8;
     /** @hide */
-    public static final int CALLBACK_SUSPENDED           = BASE + 9;
+    public static final int CALLBACK_SUSPENDED           = 9;
     /** @hide */
-    public static final int CALLBACK_RESUMED             = BASE + 10;
+    public static final int CALLBACK_RESUMED             = 10;
     /** @hide */
-    public static final int CALLBACK_BLK_CHANGED         = BASE + 11;
+    public static final int CALLBACK_BLK_CHANGED         = 11;
 
     /** @hide */
     public static String getCallbackName(int whichCallback) {
@@ -4257,15 +4267,33 @@
     }
 
     /**
-     * @hide
+     * Registers to receive notifications about the best matching network which satisfy the given
+     * {@link NetworkRequest}.  The callbacks will continue to be called until
+     * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+     * called.
+     *
+     * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+     * number of outstanding requests to 100 per app (identified by their UID), shared with
+     * {@link #registerNetworkCallback} and its variants and {@link #requestNetwork} as well as
+     * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+     * Requesting a network with this method will count toward this limit. If this limit is
+     * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+     * make sure to unregister the callbacks with
+     * {@link #unregisterNetworkCallback(NetworkCallback)}.
+     *
+     *
+     * @param request {@link NetworkRequest} describing this request.
+     * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+     *                        networks change state.
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @throws RuntimeException if the app already has too many callbacks registered.
      */
-    // TODO: Make it public api.
     @SuppressLint("ExecutorRegistration")
     public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         final NetworkCapabilities nc = request.networkCapabilities;
         final CallbackHandler cbHandler = new CallbackHandler(handler);
-        sendRequestForNetwork(nc, networkCallback, 0, TRACK_BEST, TYPE_NONE, cbHandler);
+        sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler);
     }
 
     /**
@@ -5065,19 +5093,6 @@
     }
 
     /**
-     * Listener for {@link #setOemNetworkPreference(OemNetworkPreferences, Executor,
-     * OnSetOemNetworkPreferenceListener)}.
-     * @hide
-     */
-    @SystemApi
-    public interface OnSetOemNetworkPreferenceListener {
-        /**
-         * Called when setOemNetworkPreference() successfully completes.
-         */
-        void onComplete();
-    }
-
-    /**
      * Used by automotive devices to set the network preferences used to direct traffic at an
      * application level as per the given OemNetworkPreferences. An example use-case would be an
      * automotive OEM wanting to provide connectivity for applications critical to the usage of a
@@ -5099,16 +5114,16 @@
     @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE)
     public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference,
             @Nullable @CallbackExecutor final Executor executor,
-            @Nullable final OnSetOemNetworkPreferenceListener listener) {
+            @Nullable final Runnable listener) {
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         if (null != listener) {
             Objects.requireNonNull(executor, "Executor must be non-null");
         }
-        final IOnSetOemNetworkPreferenceListener listenerInternal = listener == null ? null :
-                new IOnSetOemNetworkPreferenceListener.Stub() {
+        final IOnCompleteListener listenerInternal = listener == null ? null :
+                new IOnCompleteListener.Stub() {
                     @Override
                     public void onComplete() {
-                        executor.execute(listener::onComplete);
+                        executor.execute(listener::run);
                     }
         };
 
@@ -5120,10 +5135,56 @@
         }
     }
 
+    /**
+     * Request that a user profile is put by default on a network matching a given preference.
+     *
+     * See the documentation for the individual preferences for a description of the supported
+     * behaviors.
+     *
+     * @param profile the profile concerned.
+     * @param preference the preference for this profile.
+     * @param executor an executor to execute the listener on. Optional if listener is null.
+     * @param listener an optional listener to listen for completion of the operation.
+     * @throws IllegalArgumentException if {@code profile} is not a valid user profile.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @hide
+     */
+    // This function is for establishing per-profile default networking and can only be called by
+    // the device policy manager, running as the system server. It would make no sense to call it
+    // on a context for a user because it does not establish a setting on behalf of a user, rather
+    // it establishes a setting for a user on behalf of the DPM.
+    @SuppressLint({"UserHandle"})
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+    public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+            @ProfileNetworkPreference final int preference,
+            @Nullable @CallbackExecutor final Executor executor,
+            @Nullable final Runnable listener) {
+        if (null != listener) {
+            Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
+        }
+        final IOnCompleteListener proxy;
+        if (null == listener) {
+            proxy = null;
+        } else {
+            proxy = new IOnCompleteListener.Stub() {
+                @Override
+                public void onComplete() {
+                    executor.execute(listener::run);
+                }
+            };
+        }
+        try {
+            mService.setProfileNetworkPreference(profile, preference, proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     // The first network ID of IPSec tunnel interface.
-    private static final int TUN_INTF_NETID_START = 0xFC00;
+    private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512
     // The network ID range of IPSec tunnel interface.
-    private static final int TUN_INTF_NETID_RANGE = 0x0400;
+    private static final int TUN_INTF_NETID_RANGE = 0x0400; // 0x0400 = 1024
 
     /**
      * Get the network ID range reserved for IPSec tunnel interfaces.
@@ -5140,8 +5201,7 @@
     /**
      * Get private DNS mode from settings.
      *
-     * @param context The Context to get its ContentResolver to query the private DNS mode from
-     *                settings.
+     * @param context The Context to query the private DNS mode from settings.
      * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
      *
      * @hide
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
new file mode 100644
index 0000000..d454365
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * A manager class for connectivity module settings.
+ *
+ * @hide
+ */
+public class ConnectivitySettingsManager {
+
+    private ConnectivitySettingsManager() {}
+
+    /**
+     * Whether to automatically switch away from wifi networks that lose Internet access.
+     * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
+     * avoids such networks. Valid values are:
+     *
+     * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+     * null: Ask the user whether to switch away from bad wifi.
+     * 1: Avoid bad wifi.
+     */
+    public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+
+    /**
+     * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
+     * overridden by the system based on device or application state. If null, the value
+     * specified by config_networkMeteredMultipathPreference is used.
+     */
+    public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
+            "network_metered_multipath_preference";
+}
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index f9393e3..d83cc16 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -20,7 +20,7 @@
 import android.net.ConnectionInfo;
 import android.net.ConnectivityDiagnosticsManager;
 import android.net.IConnectivityDiagnosticsCallback;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
 import android.net.INetworkActivityListener;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
@@ -30,6 +30,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkState;
 import android.net.NetworkStateSnapshot;
 import android.net.OemNetworkPreferences;
@@ -42,6 +43,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 
 import com.android.connectivity.aidl.INetworkAgent;
 
@@ -138,7 +140,7 @@
     void declareNetworkRequestUnfulfillable(in NetworkRequest request);
 
     Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
-            in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+            in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config,
             in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
@@ -214,5 +216,8 @@
     void unregisterQosCallback(in IQosCallback callback);
 
     void setOemNetworkPreference(in OemNetworkPreferences preference,
-            in IOnSetOemNetworkPreferenceListener listener);
+            in IOnCompleteListener listener);
+
+    void setProfileNetworkPreference(in UserHandle profile, int preference,
+            in IOnCompleteListener listener);
 }
diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
index 46141e0..7245db3 100644
--- a/packages/Connectivity/framework/src/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -30,10 +30,10 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.okhttp.internalandroidapi.Dns;
-import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
 import libcore.io.IoUtils;
+import libcore.net.http.Dns;
+import libcore.net.http.HttpURLConnectionFactory;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -299,7 +299,7 @@
         // Set configuration on the HttpURLConnectionFactory that will be good for all
         // connections created by this Network. Configuration that might vary is left
         // until openConnection() and passed as arguments.
-        HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
+        HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance();
         urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
         // A private connection pool just for this Network.
         urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 27aa15d..a127c6f 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -37,7 +37,6 @@
 import com.android.connectivity.aidl.INetworkAgent;
 import com.android.connectivity.aidl.INetworkAgentRegistry;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -125,7 +124,10 @@
      */
     public final int providerId;
 
-    private static final int BASE = Protocol.BASE_NETWORK_AGENT;
+    // ConnectivityService parses message constants from itself and NetworkAgent with MessageUtils
+    // for debugging purposes, and crashes if some messages have the same values.
+    // TODO: have ConnectivityService store message names in different maps and remove this base
+    private static final int BASE = 200;
 
     /**
      * Sent by ConnectivityService to the NetworkAgent to inform it of
@@ -371,6 +373,14 @@
         return ni;
     }
 
+    // Temporary backward compatibility constructor
+    public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+        this(context, looper, logTag, nc, lp,
+                new NetworkScore.Builder().setLegacyInt(score).build(), config, provider);
+    }
+
     /**
      * Create a new network agent.
      * @param context a {@link Context} to get system services from.
@@ -382,10 +392,12 @@
      * @param score the initial score of this network. Update with sendNetworkScore.
      * @param config an immutable {@link NetworkAgentConfig} for this agent.
      * @param provider the {@link NetworkProvider} managing this agent.
+     * @hide TODO : unhide when impl is complete
      */
     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config,
+            @Nullable NetworkProvider provider) {
         this(looper, context, logTag, nc, lp, score, config,
                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
                 getLegacyNetworkInfo(config));
@@ -395,12 +407,12 @@
         public final Context context;
         public final NetworkCapabilities capabilities;
         public final LinkProperties properties;
-        public final int score;
+        public final NetworkScore score;
         public final NetworkAgentConfig config;
         public final NetworkInfo info;
         InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
-                @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
-                @NonNull NetworkInfo info) {
+                @NonNull LinkProperties properties, @NonNull NetworkScore score,
+                @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) {
             this.context = context;
             this.capabilities = capabilities;
             this.properties = properties;
@@ -412,8 +424,9 @@
     private volatile InitialConfiguration mInitialConfiguration;
 
     private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId,
+            @NonNull NetworkInfo ni) {
         mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mNetworkInfo = new NetworkInfo(ni);
@@ -875,13 +888,22 @@
     /**
      * Must be called by the agent to update the score of this network.
      *
+     * @param score the new score.
+     * @hide TODO : unhide when impl is complete
+     */
+    public final void sendNetworkScore(@NonNull NetworkScore score) {
+        Objects.requireNonNull(score);
+        queueOrSendMessage(reg -> reg.sendScore(score));
+    }
+
+    /**
+     * Must be called by the agent to update the score of this network.
+     *
      * @param score the new score, between 0 and 99.
+     * deprecated use sendNetworkScore(NetworkScore) TODO : remove in S.
      */
     public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
-        if (score < 0) {
-            throw new IllegalArgumentException("Score must be >= 0");
-        }
-        queueOrSendMessage(reg -> reg.sendScore(score));
+        sendNetworkScore(new NetworkScore.Builder().setLegacyInt(score).build());
     }
 
     /**
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 3fd95ee..dbe3ecc 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -140,7 +140,7 @@
         REQUEST,
         BACKGROUND_REQUEST,
         TRACK_SYSTEM_DEFAULT,
-        TRACK_BEST,
+        LISTEN_FOR_BEST,
     };
 
     /**
@@ -514,6 +514,15 @@
     }
 
     /**
+     * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST.
+     *
+     * @hide
+     */
+    public boolean isListenForBest() {
+        return type == Type.LISTEN_FOR_BEST;
+    }
+
+    /**
      * Returns true iff. the contained NetworkRequest is one that:
      *
      *     - should be associated with at most one satisfying network
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 48bd297..5a76cd6 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -73,6 +73,14 @@
     private final Bundle mNetworkMappings;
 
     /**
+     * Return whether this object is empty.
+     * @hide
+     */
+    public boolean isEmpty() {
+        return mNetworkMappings.keySet().size() == 0;
+    }
+
+    /**
      * Return the currently built application package name to {@link OemNetworkPreference} mappings.
      * @return the current network preferences map.
      */
diff --git a/packages/Connectivity/framework/src/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
index bcfdd7e..ca6d012 100644
--- a/packages/Connectivity/framework/src/android/net/ParseException.java
+++ b/packages/Connectivity/framework/src/android/net/ParseException.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 
 /**
  * Thrown when parsing failed.
@@ -25,12 +26,16 @@
 public class ParseException extends RuntimeException {
     public String response;
 
-    ParseException(@NonNull String response) {
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public ParseException(@NonNull String response) {
         super(response);
         this.response = response;
     }
 
-    ParseException(@NonNull String response, @NonNull Throwable cause) {
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public ParseException(@NonNull String response, @NonNull Throwable cause) {
         super(response, cause);
         this.response = response;
     }
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index c510079..cd8f4c0 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -22,9 +22,6 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.SparseArray;
-
-import com.android.internal.util.MessageUtils;
 
 import java.util.Objects;
 
@@ -38,9 +35,6 @@
  */
 @SystemApi(client = MODULE_LIBRARIES)
 public final class VpnTransportInfo implements TransportInfo, Parcelable {
-    private static final SparseArray<String> sTypeToString =
-            MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
-
     /** Type of this VPN. */
     public final int type;
 
@@ -63,8 +57,7 @@
 
     @Override
     public String toString() {
-        final String typeString = sTypeToString.get(type, "VPN_TYPE_???");
-        return String.format("VpnTransportInfo{%s}", typeString);
+        return String.format("VpnTransportInfo{type=%d}", type);
     }
 
     @Override
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 739ddad..6a49aa2 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -16,8 +16,8 @@
 
 package android.net.util;
 
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
 
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
@@ -110,8 +110,8 @@
         mHandler = handler;
         mAvoidBadWifiCallback = avoidBadWifiCallback;
         mSettingsUris = Arrays.asList(
-            Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
-            Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
+                Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+                Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
         mResolver = mContext.getContentResolver();
         mSettingObserver = new SettingObserver();
         mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
index f0193db..18d26a7 100644
--- a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -19,11 +19,12 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkScore;
 import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
 
 /**
- * Interface for NetworkAgents to send network network properties.
+ * Interface for NetworkAgents to send network properties.
  * @hide
  */
 oneway interface INetworkAgentRegistry {
@@ -31,7 +32,7 @@
     void sendLinkProperties(in LinkProperties lp);
     // TODO: consider replacing this by "markConnected()" and removing
     void sendNetworkInfo(in NetworkInfo info);
-    void sendScore(int score);
+    void sendScore(in NetworkScore score);
     void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
     void sendSocketKeepaliveEvent(int slot, int reason);
     void sendUnderlyingNetworks(in @nullable List<Network> networks);
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
index 7d98c76..06c8192 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -42,4 +42,14 @@
         -->
     </string-array>
 
+    <string-array translatable="false" name="config_legacy_networktype_restore_timers">
+        <item>2,60000</item><!-- mobile_mms -->
+        <item>3,60000</item><!-- mobile_supl -->
+        <item>4,60000</item><!-- mobile_dun -->
+        <item>5,60000</item><!-- mobile_hipri -->
+        <item>10,60000</item><!-- mobile_fota -->
+        <item>11,60000</item><!-- mobile_ims -->
+        <item>12,60000</item><!-- mobile_cbs -->
+    </string-array>
+
 </resources>
\ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
index 00ec2df..da8aee5 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -17,11 +17,11 @@
     <overlayable name="ServiceConnectivityResourcesConfig">
         <policy type="product|system|vendor">
             <!-- Configuration values for ConnectivityService -->
+            <item type="array" name="config_legacy_networktype_restore_timers"/>
             <item type="string" name="config_networkCaptivePortalServerUrl"/>
             <item type="integer" name="config_networkTransitionTimeout"/>
             <item type="array" name="config_wakeonlan_supported_interfaces"/>
 
-
         </policy>
     </overlayable>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 9c0c80b..bf4242f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -23,7 +23,6 @@
 import android.graphics.drawable.Drawable;
 import android.location.LocationManager;
 import android.media.AudioManager;
-import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
 import android.net.TetheringManager;
 import android.net.vcn.VcnTransportInfo;
@@ -37,6 +36,7 @@
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 
 import androidx.annotation.NonNull;
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -439,8 +439,7 @@
     }
 
     public static boolean isWifiOnly(Context context) {
-        return !context.getSystemService(ConnectivityManager.class)
-                .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        return !context.getSystemService(TelephonyManager.class).isDataCapable();
     }
 
     /** Returns if the automatic storage management feature is turned on or not. **/
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 092cbf3..60bcf37 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,7 +16,6 @@
 
 package com.android.settingslib.net;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -59,7 +58,6 @@
             PERIOD_BUILDER, Locale.getDefault());
 
     private final Context mContext;
-    private final ConnectivityManager mConnectivityManager;
     private final INetworkStatsService mStatsService;
     private final NetworkPolicyManager mPolicyManager;
     private final NetworkStatsManager mNetworkStatsManager;
@@ -71,7 +69,6 @@
 
     public DataUsageController(Context context) {
         mContext = context;
-        mConnectivityManager = ConnectivityManager.from(context);
         mStatsService = INetworkStatsService.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         mPolicyManager = NetworkPolicyManager.from(mContext);
@@ -236,7 +233,7 @@
 
     public boolean isMobileDataSupported() {
         // require both supported network and ready SIM
-        return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
+        return getTelephonyManager().isDataCapable()
                 && getTelephonyManager().getSimState() == SIM_STATE_READY;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 841a49e..cbfd4d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -75,7 +75,8 @@
             .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
             .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
             .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
-    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+    private final NetworkCallback mNetworkCallback =
+            new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
         @Override
         public void onAvailable(
                 Network network, NetworkCapabilities networkCapabilities,
@@ -131,7 +132,8 @@
             }
         }
     };
-    private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
+    private final NetworkCallback mDefaultNetworkCallback =
+            new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
         @Override
         public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
             // network is now the default network, and its capabilities are nc.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6568bff..268603f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2445,8 +2445,8 @@
                     R.bool.def_auto_time_zone); // Sync timezone to NITZ
 
             loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
-                    ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
-                        res.getBoolean(R.bool.def_stay_on_while_plugged_in))
+                    ("1".equals(SystemProperties.get("ro.boot.qemu"))
+                        || res.getBoolean(R.bool.def_stay_on_while_plugged_in))
                      ? 1 : 0);
 
             loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml
index fe2ffaa..bd91ef9 100644
--- a/packages/SystemUI/res/color/remote_input_send.xml
+++ b/packages/SystemUI/res/color/remote_input_send.xml
@@ -16,6 +16,6 @@
   -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="true" android:color="@android:color/white" />
-    <item android:color="#4dffffff" /> <!-- 30% white -->
+    <item android:state_enabled="true" android:color="?android:attr/colorAccent" />
+    <item android:color="?android:attr/colorAccent" android:alpha=".3" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
index 8e18e16..33eeb77 100644
--- a/packages/SystemUI/res/color/remote_input_text.xml
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -16,6 +16,6 @@
   -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="true" android:color="@color/remote_input_text_enabled" /> <!-- white -->
-    <item android:color="#99ffffff" /> <!-- 60% white -->
+    <item android:state_enabled="true" android:color="?android:attr/textColorTertiary" />
+    <item android:color="?android:attr/textColorTertiary" android:alpha=".6" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index b5d48b4..43182eb 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -19,7 +19,6 @@
 <!-- LinearLayout -->
 <com.android.systemui.statusbar.policy.RemoteInputView
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:theme="@style/systemui_theme_remote_input"
         android:id="@+id/remote_input"
         android:layout_height="match_parent"
         android:layout_width="match_parent">
@@ -33,6 +32,10 @@
             android:paddingBottom="4dp"
             android:paddingStart="16dp"
             android:paddingEnd="12dp"
+            android:layout_marginRight="5dp"
+            android:layout_marginLeft="20dp"
+            android:layout_marginTop="5dp"
+            android:layout_marginBottom="20dp"
             android:gravity="start|center_vertical"
             android:textAppearance="?android:attr/textAppearance"
             android:textColor="@color/remote_input_text"
@@ -53,6 +56,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:layout_gravity="center"
+                android:paddingBottom="20dp"
                 android:paddingStart="12dp"
                 android:paddingEnd="24dp"
                 android:id="@+id/remote_input_send"
@@ -66,6 +70,7 @@
                 android:id="@+id/remote_input_progress"
                 android:layout_width="24dp"
                 android:layout_height="24dp"
+                android:layout_marginBottom="10dp"
                 android:layout_marginEnd="6dp"
                 android:layout_gravity="center"
                 android:visibility="invisible"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4241724..d478319 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -153,10 +153,9 @@
     <color name="minimize_dock_shadow_end">#00000000</color>
 
     <color name="default_remote_input_background">@*android:color/notification_default_color</color>
-    <color name="remote_input_text_enabled">#ffffffff</color>
     <color name="remote_input_hint">#99ffffff</color>
 
-    <color name="remote_input_accent">#eeeeee</color>
+    <color name="remote_input_accent">?android:attr/colorAccent</color>
 
     <color name="quick_step_track_background_background_dark">#1F000000</color>
     <color name="quick_step_track_background_background_light">#33FFFFFF</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 57c932e..87fa4f8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1337,7 +1337,7 @@
     <string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
 
     <!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]-->
-    <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string>
+    <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device, manage apps, and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string>
 
     <!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
     <string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fb885cb..ff9ea01 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -384,10 +384,6 @@
     <!-- Overridden by values-television/styles.xml with tv-specific settings -->
     <style name="volume_dialog_theme" parent="qs_theme"/>
 
-    <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
-        <item name="android:colorAccent">@color/remote_input_accent</item>
-    </style>
-
     <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog" />
 
     <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a9b4c49..7966b38 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -25,9 +25,12 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
+import android.util.ArraySet;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Size;
 import android.view.SurfaceHolder;
+import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 
@@ -54,7 +57,10 @@
     private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS =
             new RectF(0, 0, 1, 1);
     private static final boolean DEBUG = false;
-    private ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
+    private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
+    private final ArraySet<RectF> mColorAreas = new ArraySet<>();
+    private float mShift;
+    private volatile int mPages;
     private HandlerThread mWorker;
     // scaled down version
     private Bitmap mMiniBitmap;
@@ -96,6 +102,10 @@
         private EglHelper mEglHelper;
         private final Runnable mFinishRenderingTask = this::finishRendering;
         private boolean mNeedRedraw;
+        private int mWidth = 1;
+        private int mHeight = 1;
+        private int mImgWidth = 1;
+        private int mImgHeight = 1;
 
         GLEngine() {
         }
@@ -111,8 +121,13 @@
             // Deferred init renderer because we need to get wallpaper by display context.
             mRenderer = getRendererInstance();
             setFixedSizeAllowed(true);
-            setOffsetNotificationsEnabled(false);
             updateSurfaceSize();
+            Rect window = getDisplayContext()
+                    .getSystemService(WindowManager.class)
+                    .getCurrentWindowMetrics()
+                    .getBounds();
+            mHeight = window.height();
+            mWidth = window.width();
             mMiniBitmap = null;
             if (mWorker != null && mWorker.getThreadHandler() != null) {
                 mWorker.getThreadHandler().post(this::updateMiniBitmap);
@@ -127,6 +142,41 @@
             return new ImageWallpaperRenderer(getDisplayContext());
         }
 
+        @Override
+        public void onOffsetsChanged(float xOffset, float yOffset,
+                float xOffsetStep, float yOffsetStep,
+                int xPixelOffset, int yPixelOffset) {
+            if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
+            final int pages;
+            if (xOffsetStep > 0 && xOffsetStep <= 1) {
+                pages = (int) (1 / xOffsetStep + 1);
+            } else {
+                pages = 1;
+            }
+            if (pages == mPages) return;
+            mPages = pages;
+            updateShift();
+            mWorker.getThreadHandler().post(() ->
+                    computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
+        }
+
+        private void updateShift() {
+            if (mImgHeight == 0) {
+                mShift = 0;
+                return;
+            }
+            // calculate shift
+            float imgWidth = (float) mImgWidth / (float) mImgHeight;
+            float displayWidth =
+                    (float) mWidth / (float) mHeight;
+            // if need to shift
+            if (imgWidth > displayWidth) {
+                mShift = imgWidth / imgWidth - displayWidth / imgWidth;
+            } else {
+                mShift = 0;
+            }
+        }
+
         private void updateMiniBitmap() {
             mRenderer.useBitmap(b -> {
                 int size = Math.min(b.getWidth(), b.getHeight());
@@ -134,6 +184,8 @@
                 if (size > MIN_SURFACE_WIDTH) {
                     scale = (float) MIN_SURFACE_WIDTH / (float) size;
                 }
+                mImgHeight = b.getHeight();
+                mImgWidth = b.getWidth();
                 mMiniBitmap = Bitmap.createScaledBitmap(b, Math.round(scale * b.getWidth()),
                         Math.round(scale * b.getHeight()), false);
                 computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
@@ -173,6 +225,9 @@
         @Override
         public void addLocalColorsAreas(@NonNull List<RectF> regions) {
             mWorker.getThreadHandler().post(() -> {
+                if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
+                    setOffsetNotificationsEnabled(true);
+                }
                 Bitmap bitmap = mMiniBitmap;
                 if (bitmap == null) {
                     mLocalColorsToAdd.addAll(regions);
@@ -184,6 +239,7 @@
 
         private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) {
             List<WallpaperColors> colors = getLocalWallpaperColors(regions, b);
+            mColorAreas.addAll(regions);
             try {
                 notifyLocalColorsChanged(regions, colors);
             } catch (RuntimeException e) {
@@ -193,14 +249,45 @@
 
         @Override
         public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
-            // No-OP
+            mWorker.getThreadHandler().post(() -> {
+                mColorAreas.removeAll(regions);
+                mLocalColorsToAdd.removeAll(regions);
+                if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
+                    setOffsetNotificationsEnabled(false);
+                }
+            });
+        }
+
+        private RectF pageToImgRect(RectF area) {
+            float pageWidth = 1f / (float) mPages;
+            if (pageWidth < 1 && pageWidth >= 0) pageWidth = 1;
+            float imgWidth = (float) mImgWidth / (float) mImgHeight;
+            float displayWidth =
+                    (float) mWidth / (float) mHeight;
+            float expansion = imgWidth > displayWidth ? displayWidth / imgWidth : 1;
+            int page = (int) Math.floor(area.centerX() / pageWidth);
+            float shiftWidth = mShift * page * pageWidth;
+            RectF imgArea = new RectF();
+            imgArea.bottom = area.bottom;
+            imgArea.top = area.top;
+            imgArea.left = MathUtils.constrain(area.left % pageWidth, 0, 1)
+                    * expansion + shiftWidth;
+            imgArea.right = MathUtils.constrain(area.right % pageWidth, 0, 1)
+                    * expansion + shiftWidth;
+            if (imgArea.left > imgArea.right) {
+                // take full page
+                imgArea.left = shiftWidth;
+                imgArea.right = 1 - (mShift - shiftWidth);
+            }
+            return imgArea;
         }
 
         private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
                 Bitmap b) {
             List<WallpaperColors> colors = new ArrayList<>(areas.size());
+            updateShift();
             for (int i = 0; i < areas.size(); i++) {
-                RectF area = areas.get(i);
+                RectF area = pageToImgRect(areas.get(i));
                 if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
                     colors.add(null);
                     continue;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index a78c223..378907c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -73,6 +73,16 @@
     }
 
     @Override
+    void onIlluminationStarting() {
+        setVisibility(View.INVISIBLE);
+    }
+
+    @Override
+    void onIlluminationStopped() {
+        setVisibility(View.VISIBLE);
+    }
+
+    @Override
     public boolean dozeTimeTick() {
         // TODO: burnin
         mFingerprintDrawable.dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 55c55b9..41c9dae 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -476,7 +476,6 @@
                 Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf<Int>()
         // TODO: b/153736623 look into creating actions when this isn't a media style notification
 
-        val packageContext: Context = sbn.getPackageContext(context)
         if (actions != null) {
             for ((index, action) in actions.withIndex()) {
                 if (action.getIcon() == null) {
@@ -499,7 +498,7 @@
                     null
                 }
                 val mediaActionIcon = if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) {
-                    Icon.createWithResource(packageContext, action.getIcon()!!.getResId())
+                    Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
                 } else {
                     action.getIcon()
                 }.setTint(themeText)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 2cd367d..3e3451e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -445,11 +445,6 @@
         final ArrayList<String> tiles = new ArrayList<String>();
         boolean addedDefault = false;
         Set<String> addedSpecs = new ArraySet<>();
-        // TODO(b/174753536): Move it into the config file.
-        if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-            tiles.add("internet");
-            addedSpecs.add("internet");
-        }
         for (String tile : tileList.split(",")) {
             tile = tile.trim();
             if (tile.isEmpty()) continue;
@@ -457,17 +452,6 @@
                 if (!addedDefault) {
                     List<String> defaultSpecs = getDefaultSpecs(context);
                     for (String spec : defaultSpecs) {
-                        // TODO(b/174753536): Move it into the config file.
-                        if (FeatureFlagUtils.isEnabled(
-                                context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-                            if (spec.equals("wifi") || spec.equals("cell")) {
-                                continue;
-                            }
-                        } else {
-                            if (spec.equals("internet")) {
-                                continue;
-                            }
-                        }
                         if (!addedSpecs.contains(spec)) {
                             tiles.add(spec);
                             addedSpecs.add(spec);
@@ -476,18 +460,40 @@
                     addedDefault = true;
                 }
             } else {
-                // TODO(b/174753536): Move it into the config file.
-                if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-                    if (tile.equals("wifi") || tile.equals("cell")) {
-                        continue;
-                    }
-                }
                 if (!addedSpecs.contains(tile)) {
                     tiles.add(tile);
                     addedSpecs.add(tile);
                 }
             }
         }
+        // TODO(b/174753536): Move it into the config file.
+        // Only do the below hacking when at least one of the below tiles exist
+        //   --InternetTile
+        //   --WiFiTile
+        //   --CellularTIle
+        if (tiles.contains("internet") || tiles.contains("wifi") || tiles.contains("cell")) {
+            if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+                if (!tiles.contains("internet")) {
+                    tiles.add("internet");
+                }
+                if (tiles.contains("wifi")) {
+                    tiles.remove("wifi");
+                }
+                if (tiles.contains("cell")) {
+                    tiles.remove("cell");
+                }
+            } else {
+                if (tiles.contains("internet")) {
+                    tiles.remove("internet");
+                }
+                if (!tiles.contains("wifi")) {
+                    tiles.add("wifi");
+                }
+                if (!tiles.contains("cell")) {
+                    tiles.add("cell");
+                }
+            }
+        }
         return tiles;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 7eeb4bd..32b41ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -115,9 +115,32 @@
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
         // TODO(b/174753536): Move it into the config file.
-        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-            if (!possibleTiles.contains("internet")) {
-                possibleTiles.add("internet");
+        // Only do the below hacking when at least one of the below tiles exist
+        //   --InternetTile
+        //   --WiFiTile
+        //   --CellularTIle
+        if (possibleTiles.contains("internet") || possibleTiles.contains("wifi")
+                || possibleTiles.contains("cell")) {
+            if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+                if (!possibleTiles.contains("internet")) {
+                    possibleTiles.add("internet");
+                }
+                if (possibleTiles.contains("wifi")) {
+                    possibleTiles.remove("wifi");
+                }
+                if (possibleTiles.contains("cell")) {
+                    possibleTiles.remove("cell");
+                }
+            } else {
+                if (possibleTiles.contains("internet")) {
+                    possibleTiles.remove("internet");
+                }
+                if (!possibleTiles.contains("wifi")) {
+                    possibleTiles.add("wifi");
+                }
+                if (!possibleTiles.contains("cell")) {
+                    possibleTiles.add("cell");
+                }
             }
         }
         for (String spec : possibleTiles) {
@@ -125,15 +148,6 @@
             // Do not include CustomTile. Those will be created by `addPackageTiles`.
             if (spec.startsWith(CustomTile.PREFIX)) continue;
             // TODO(b/174753536): Move it into the config file.
-            if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-                if (spec.equals("wifi") || spec.equals("cell")) {
-                    continue;
-                }
-            } else {
-                if (spec.equals("internet")) {
-                    continue;
-                }
-            }
             final QSTile tile = host.createTile(spec);
             if (tile == null) {
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 6ba5215..5219ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,16 +16,23 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.text.format.DateFormat;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.view.View;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
@@ -81,6 +88,8 @@
     // Record the HISTORY_SIZE most recent states
     private int mHistoryIndex = 0;
     private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
+    // This is used by InteractionJankMonitor to get callback from HWUI.
+    private View mView;
 
     /**
      * If any of the system bars is hidden.
@@ -236,6 +245,11 @@
 
     @Override
     public void setDozeAmount(float dozeAmount, boolean animated) {
+        setAndInstrumentDozeAmount(null, dozeAmount, animated);
+    }
+
+    @Override
+    public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
         if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
             if (animated && mDozeAmountTarget == dozeAmount) {
                 return;
@@ -244,6 +258,11 @@
             }
         }
 
+        // We don't need a new attached view if we already have one.
+        if ((mView == null || !mView.isAttachedToWindow())
+                && (view != null && view.isAttachedToWindow())) {
+            mView = view;
+        }
         mDozeAmountTarget = dozeAmount;
         if (animated) {
             startDozeAnimation();
@@ -261,6 +280,22 @@
         mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
         mDarkAnimator.setInterpolator(Interpolators.LINEAR);
         mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
+        mDarkAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                cancelInteractionJankMonitor();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                endInteractionJankMonitor();
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                beginInteractionJankMonitor();
+            }
+        });
         mDarkAnimator.start();
     }
 
@@ -277,6 +312,24 @@
         }
     }
 
+    private void beginInteractionJankMonitor() {
+        if (mView != null && mView.isAttachedToWindow()) {
+            InteractionJankMonitor.getInstance().begin(mView, getCujType());
+        }
+    }
+
+    private void endInteractionJankMonitor() {
+        InteractionJankMonitor.getInstance().end(getCujType());
+    }
+
+    private void cancelInteractionJankMonitor() {
+        InteractionJankMonitor.getInstance().cancel(getCujType());
+    }
+
+    private int getCujType() {
+        return mIsDozing ? CUJ_LOCKSCREEN_TRANSITION_TO_AOD : CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
+    }
+
     @Override
     public boolean goingToFullShade() {
         return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index a2e07b2..b6d6ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -19,6 +19,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.view.View;
 
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -75,6 +76,15 @@
      */
     void setDozeAmount(float dozeAmount, boolean animated);
 
+    /**
+     * Changes the current doze amount, also starts the
+     * {@link com.android.internal.jank.InteractionJankMonitor InteractionJankMonitor} as possible.
+     *
+     * @param view An attached view, which will be used by InteractionJankMonitor.
+     * @param dozeAmount New doze/dark amount.
+     * @param animated If change should be animated or not. This will cancel current animations.
+     */
+    void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated);
 
     /**
      * Update the expanded state from {@link StatusBar}'s perspective
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index fdd8f34..58b87cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -27,8 +27,10 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.AsyncTask;
 import android.os.CancellationSignal;
+import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.View;
@@ -768,10 +770,26 @@
             return mReInflateFlags;
         }
 
+        void updateApplicationInfo(StatusBarNotification sbn) {
+            String packageName = sbn.getPackageName();
+            int userId = UserHandle.getUserId(sbn.getUid());
+            final ApplicationInfo appInfo;
+            try {
+                // This method has an internal cache, so we don't need to add our own caching here.
+                appInfo = mContext.getPackageManager().getApplicationInfoAsUser(packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+            } catch (PackageManager.NameNotFoundException e) {
+                return;
+            }
+            Notification.addFieldsFromContext(appInfo, sbn.getNotification());
+        }
+
         @Override
         protected InflationProgress doInBackground(Void... params) {
             try {
                 final StatusBarNotification sbn = mEntry.getSbn();
+                // Ensure the ApplicationInfo is updated before a builder is recovered.
+                updateApplicationInfo(sbn);
                 final Notification.Builder recoveredBuilder
                         = Notification.Builder.recoverBuilder(mContext,
                         sbn.getNotification());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 8c21e76..55a27b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -22,8 +22,6 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -32,7 +30,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
@@ -44,7 +41,6 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -1274,23 +1270,6 @@
                 }
             }
             if (hasRemoteInput) {
-                int color = entry.getSbn().getNotification().color;
-                if (color == Notification.COLOR_DEFAULT) {
-                    color = mContext.getColor(R.color.default_remote_input_background);
-                }
-                if (mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.config_tintNotificationsWithTheme)) {
-                    Resources.Theme theme = new ContextThemeWrapper(mContext,
-                            com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
-                    TypedArray ta = theme.obtainStyledAttributes(
-                            new int[]{com.android.internal.R.attr.colorAccent});
-                    color = ta.getColor(0, color);
-                    ta.recycle();
-                }
-                existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
-                        mContext.getColor(R.color.remote_input_text_enabled),
-                        mContext.getColor(R.color.remote_input_hint)));
-
                 existing.setWrapper(wrapper);
                 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
 
@@ -1312,6 +1291,10 @@
                     }
                 }
             }
+
+            if (existing != null && entry.getSbn().getNotification().isColorized()) {
+                existing.overrideBackgroundTintColor(entry.getSbn().getNotification().color);
+            }
             return existing;
         }
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index acb3e57..cd71204 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3127,7 +3127,7 @@
         }
 
         final float dozeAmount = dozing ? 1 : 0;
-        mStatusBarStateController.setDozeAmount(dozeAmount, animate);
+        mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate);
     }
 
     public void setPulsing(boolean pulsing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index fbdaf9c..db039b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -31,6 +31,7 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkScoreManager;
@@ -307,7 +308,8 @@
             mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback);
         }
 
-        ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
+        NetworkCallback callback =
+                new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO){
             private Network mLastNetwork;
             private NetworkCapabilities mLastNetworkCapabilities;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 320b00a..f72d2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -31,8 +31,15 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ServiceManager;
@@ -70,6 +77,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -137,12 +145,40 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
+    /**
+     * The remote view needs to adapt to colorized notifications when set
+     * @param color colorized notification color
+     */
+    public void overrideBackgroundTintColor(int color) {
+        mEditText.setBackgroundTintColor(color);
+        final boolean dark = !ContrastColorUtil.isColorLight(color);
+        int[][] states = new int[][] {
+                new int[] {android.R.attr.state_enabled},
+                new int[] {},
+        };
+
+        final int finalColor = dark
+                ? Color.WHITE
+                : Color.BLACK;
+
+        int[] colors = new int[] {
+                finalColor,
+                finalColor & 0x4DFFFFFF // %30 opacity
+        };
+
+        final ColorStateList tint = new ColorStateList(states, colors);
+        mSendButton.setImageTintList(tint);
+        mProgressBar.setProgressTintList(tint);
+        mProgressBar.setIndeterminateTintList(tint);
+        mProgressBar.setSecondaryProgressTintList(tint);
+        mEditText.setForegroundColor(finalColor);
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
         mProgressBar = findViewById(R.id.remote_input_progress);
-
         mSendButton = findViewById(R.id.remote_input_send);
         mSendButton.setOnClickListener(this);
 
@@ -362,6 +398,12 @@
         }
     }
 
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mEditText.updateCornerRadius(heightMeasureSpec / 2);
+    }
+
     /** Populates the text field of the remote input with the given content. */
     public void setEditTextContent(@Nullable CharSequence editTextContent) {
         mEditText.setText(editTextContent);
@@ -651,17 +693,47 @@
 
         private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent;
 
-        private final Drawable mBackground;
         private RemoteInputView mRemoteInputView;
+        private GradientDrawable mTextBackground;
+        private ColorDrawable mBackgroundColor;
+        private LayerDrawable mBackground;
         boolean mShowImeOnInputConnection;
         private LightBarController mLightBarController;
         private InputMethodManager mInputMethodManager;
+        private int mColor = Notification.COLOR_DEFAULT;
         UserHandle mUser;
+        private int mStokeWidth;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
-            mBackground = getBackground();
             mLightBarController = Dependency.get(LightBarController.class);
+            mTextBackground = createBackground(context, attrs);
+            mBackgroundColor = new ColorDrawable();
+            mBackground = new LayerDrawable(new Drawable[] {mBackgroundColor, mTextBackground});
+            float density = context.getResources().getDisplayMetrics().density;
+            mStokeWidth = (int) (2 * density);
+            setDefaultColors();
+        }
+
+        private void setDefaultColors() {
+            Resources.Theme theme = getContext().getTheme();
+            TypedArray ta = theme.obtainStyledAttributes(
+                    new int[]{android.R.attr.colorAccent,
+                            com.android.internal.R.attr.colorBackgroundFloating});
+            mTextBackground.setStroke(mStokeWidth,
+                    ta.getColor(0, Notification.COLOR_DEFAULT));
+            mColor = ta.getColor(1, Notification.COLOR_DEFAULT);
+            mTextBackground.setColor(mColor);
+        }
+
+        private GradientDrawable createBackground(Context context, AttributeSet attrs) {
+            float density = context.getResources().getDisplayMetrics().density;
+            int padding = (int) (12 * density);
+            GradientDrawable d = new GradientDrawable();
+            d.setShape(GradientDrawable.RECTANGLE);
+            d.setPadding(padding, padding, padding, padding);
+            d.setCornerRadius(padding);
+            return d;
         }
 
         void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) {
@@ -724,6 +796,19 @@
             }
         }
 
+        protected void setBackgroundTintColor(int color) {
+            mBackgroundColor.setColor(color);
+            mTextBackground.setColor(color);
+        }
+
+        protected void setForegroundColor(int color) {
+            mTextBackground.setStroke(mStokeWidth, color);
+            setTextColor(color);
+            // %60
+            setHintTextColor(color & 0x99FFFFFF);
+            setTextCursorDrawable(null);
+        }
+
         @Override
         public void getFocusedRect(Rect r) {
             super.getFocusedRect(r);
@@ -811,6 +896,10 @@
             setSelection(getText().length());
         }
 
+        void updateCornerRadius(float radius) {
+            mTextBackground.setCornerRadius(radius);
+        }
+
         void setInnerFocusable(boolean focusable) {
             setFocusableInTouchMode(focusable);
             setFocusable(focusable);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index b1f1b5e..116e1b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -917,11 +917,11 @@
         HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
                 ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
                 ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
-                ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED));
+                ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
 
         for (ScrimState state : ScrimState.values()) {
             if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
-                Assert.fail("Scrim state not whitelisted nor blacklisted as low power mode");
+                Assert.fail("Scrim state isn't categorized as a low power or regular state.");
             }
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index c7f0efa..3d07da5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -118,6 +118,7 @@
 
         private static final int INVALID_ID = -1;
         private int mIdOfLastServiceToMagnify = INVALID_ID;
+        private boolean mMagnificationActivated = false;
 
         DisplayMagnification(int displayId) {
             mDisplayId = displayId;
@@ -322,6 +323,13 @@
                         mSpecAnimationBridge, spec, animationCallback);
                 mControllerCtx.getHandler().sendMessage(m);
             }
+
+            final boolean lastMagnificationActivated = mMagnificationActivated;
+            mMagnificationActivated = spec.scale > 1.0f;
+            if (mMagnificationActivated != lastMagnificationActivated) {
+                mMagnificationRequestObserver.onFullScreenMagnificationActivationState(
+                        mMagnificationActivated);
+            }
         }
 
         /**
@@ -1506,5 +1514,14 @@
          * @param serviceId the ID of the service requesting the change
          */
         void onRequestMagnificationSpec(int displayId, int serviceId);
+
+        /**
+         * Called when the state of the magnification activation is changed.
+         * It is for the logging data of the magnification activation state.
+         *
+         * @param activated {@code true} if the magnification is activated, otherwise {@code false}.
+         */
+        @GuardedBy("mLock")
+        void onFullScreenMagnificationActivationState(boolean activated);
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 2a65b64..17a7d39 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 
@@ -25,11 +26,13 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.accessibility.MagnificationAnimationCallback;
 
+import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.accessibility.AccessibilityManagerService;
 
@@ -51,7 +54,8 @@
  * </ol>
  */
 public class MagnificationController implements WindowMagnificationManager.Callback,
-        MagnificationGestureHandler.Callback {
+        MagnificationGestureHandler.Callback,
+        FullScreenMagnificationController.MagnificationRequestObserver {
 
     private static final boolean DEBUG = false;
     private static final String TAG = "MagnificationController";
@@ -66,6 +70,9 @@
     private WindowMagnificationManager mWindowMagnificationMgr;
     private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 
+    private long mWindowModeEnabledTime = 0;
+    private long mFullScreenModeEnabledTime = 0;
+
     /**
      * A callback to inform the magnification transition result.
      */
@@ -187,7 +194,8 @@
         setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
     }
 
-    void onRequestMagnificationSpec(int displayId, int serviceId) {
+    @Override
+    public void onRequestMagnificationSpec(int displayId, int serviceId) {
         synchronized (mLock) {
             if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) {
                 return;
@@ -200,6 +208,39 @@
         }
     }
 
+    // TODO : supporting multi-display (b/182227245).
+    @Override
+    public void onWindowMagnificationActivationState(boolean activated) {
+        if (activated) {
+            mWindowModeEnabledTime = SystemClock.uptimeMillis();
+        } else {
+            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+                    SystemClock.uptimeMillis() - mWindowModeEnabledTime);
+        }
+    }
+
+    @Override
+    public void onFullScreenMagnificationActivationState(boolean activated) {
+        if (activated) {
+            mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
+        } else {
+            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
+                    SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
+        }
+    }
+
+    /**
+     * Wrapper method of logging the magnification activated mode and its duration of the usage
+     * when the magnification is disabled.
+     *
+     * @param mode The activated magnification mode.
+     * @param duration The duration in milliseconds during the magnification is activated.
+     */
+    @VisibleForTesting
+    public void logMagnificationUsageState(int mode, long duration) {
+        AccessibilityStatsLogUtils.logMagnificationUsageState(mode, duration);
+    }
+
     /**
      * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
      * WindowMagnificationManager}.
@@ -260,7 +301,7 @@
         synchronized (mLock) {
             if (mFullScreenMagnificationController == null) {
                 mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
-                        mAms, mLock, this::onRequestMagnificationSpec);
+                        mAms, mLock, this);
                 mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
             }
         }
@@ -340,7 +381,7 @@
             mTransitionCallBack = transitionCallBack;
             mDisplayId = displayId;
             mTargetMode = targetMode;
-            mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+            mCurrentMode = mTargetMode ^ ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
             mCurrentScale = scale;
             mCurrentCenter.set(currentCenter);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 40668d8..ded601e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -93,6 +93,13 @@
          * @param scale the target scale, or {@link Float#NaN} to leave unchanged
          */
         void onPerformScaleAction(int displayId, float scale);
+
+        /**
+         * Called when the state of the magnification activation is changed.
+         *
+         * @param activated {@code true} if the magnification is activated, otherwise {@code false}.
+         */
+        void onWindowMagnificationActivationState(boolean activated);
     }
 
     private final Callback mCallback;
@@ -264,6 +271,7 @@
      */
     void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
             @Nullable MagnificationAnimationCallback animationCallback) {
+        final boolean enabled;
         synchronized (mLock) {
             if (mConnectionWrapper == null) {
                 return;
@@ -272,9 +280,13 @@
             if (magnifier == null) {
                 magnifier = createWindowMagnifier(displayId);
             }
-            magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
+            enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
                     animationCallback);
         }
+
+        if (enabled) {
+            mCallback.onWindowMagnificationActivationState(true);
+        }
     }
 
     /**
@@ -296,16 +308,21 @@
      */
     void disableWindowMagnification(int displayId, boolean clear,
             MagnificationAnimationCallback animationCallback) {
+        final boolean disabled;
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null || mConnectionWrapper == null) {
                 return;
             }
-            magnifier.disableWindowMagnificationInternal(animationCallback);
+            disabled = magnifier.disableWindowMagnificationInternal(animationCallback);
             if (clear) {
                 mWindowMagnifiers.delete(displayId);
             }
         }
+
+        if (disabled) {
+            mCallback.onWindowMagnificationActivationState(false);
+        }
     }
 
     /**
@@ -560,26 +577,35 @@
         }
 
         @GuardedBy("mLock")
-        void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
+        boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
                 @Nullable MagnificationAnimationCallback animationCallback) {
             if (mEnabled) {
-                return;
+                return false;
             }
             final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
             if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
                     centerX, centerY, animationCallback)) {
                 mScale = normScale;
                 mEnabled = true;
+
+                return true;
             }
+            return false;
         }
 
         @GuardedBy("mLock")
-        void disableWindowMagnificationInternal(
+        boolean disableWindowMagnificationInternal(
                 @Nullable MagnificationAnimationCallback animationResultCallback) {
-            if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
+            if (!mEnabled) {
+                return false;
+            }
+            if (mWindowMagnificationManager.disableWindowMagnificationInternal(
                     mDisplayId, animationResultCallback)) {
                 mEnabled = false;
+
+                return true;
             }
+            return false;
         }
 
         @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
index 2dcf82f..611a37d 100644
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -17,6 +17,9 @@
 package com.android.server;
 
 import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import java.util.ArrayList;
 
 /**
  * The BluetoothDeviceConfigListener handles system device config change callback and checks
@@ -30,10 +33,12 @@
 class BluetoothDeviceConfigListener {
     private static final String TAG = "BluetoothDeviceConfigListener";
 
-    BluetoothManagerService mService;
+    private final BluetoothManagerService mService;
+    private final boolean mLogDebug;
 
-    BluetoothDeviceConfigListener(BluetoothManagerService service) {
+    BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
         mService = service;
+        mLogDebug = logDebug;
         DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_BLUETOOTH,
                 (Runnable r) -> r.run(),
@@ -47,6 +52,13 @@
                     if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
                         return;
                     }
+                    if (mLogDebug) {
+                        ArrayList<String> flags = new ArrayList<>();
+                        for (String name : properties.getKeyset()) {
+                            flags.add(name + "='" + properties.getString(name, "") + "'");
+                        }
+                        Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
+                    }
                     boolean foundInit = false;
                     for (String name : properties.getKeyset()) {
                         if (name.startsWith("INIT_")) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index dc24ffd..f0c9ba9 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -454,6 +454,7 @@
                 if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
                         && state == BluetoothProfile.STATE_DISCONNECTED
                         && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                    Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
                     onInitFlagsChanged();
                 }
             }
@@ -820,6 +821,35 @@
         return enabledProfiles;
     }
 
+    private boolean isDeviceProvisioned() {
+        return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
+                0) != 0;
+    }
+
+    // Monitor change of BLE scan only mode settings.
+    private void registerForProvisioningStateChange() {
+        ContentObserver contentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                if (!isDeviceProvisioned()) {
+                    if (DBG) {
+                        Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
+                                + "provisioned");
+                    }
+                    return;
+                }
+                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
+                    Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
+                    onInitFlagsChanged();
+                }
+            }
+        };
+
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
+                contentObserver);
+    }
+
     // Monitor change of BLE scan only mode settings.
     private void registerForBleScanModeChange() {
         ContentObserver contentObserver = new ContentObserver(null) {
@@ -1385,7 +1415,8 @@
         if (mBluetoothAirplaneModeListener != null) {
             mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
         }
-        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
+        registerForProvisioningStateChange();
+        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
     }
 
     /**
@@ -2229,12 +2260,25 @@
                     }
                     mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
                     if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+                                + " ms due to existing connections");
+                        mHandler.sendEmptyMessageDelayed(
+                                MESSAGE_INIT_FLAGS_CHANGED,
+                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+                        break;
+                    }
+                    if (!isDeviceProvisioned()) {
+                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+                                +  "ms because device is not provisioned");
                         mHandler.sendEmptyMessageDelayed(
                                 MESSAGE_INIT_FLAGS_CHANGED,
                                 DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
                         break;
                     }
                     if (mBluetooth != null && isEnabled()) {
+                        Slog.i(TAG, "Restarting Bluetooth due to init flag change");
                         restartForReason(
                                 BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
                     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 688b33e..3194bdc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,10 @@
 package com.android.server;
 
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
@@ -28,15 +32,30 @@
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
 import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.TYPE_VPN;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
@@ -55,6 +74,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
 import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.VPN_UID;
@@ -86,6 +106,7 @@
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivitySettingsManager;
 import android.net.DataStallReportParcelable;
 import android.net.DnsResolverServiceManager;
 import android.net.ICaptivePortal;
@@ -97,7 +118,7 @@
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
 import android.net.InetAddresses;
@@ -110,13 +131,13 @@
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
@@ -191,13 +212,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.PermissionUtils;
 import com.android.server.connectivity.AutodestructReference;
@@ -213,6 +234,7 @@
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
+import com.android.server.connectivity.ProfileNetworkPreferences;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.QosCallbackTracker;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -557,8 +579,8 @@
     private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
 
     /**
-     * used internally when setting the default networks for OemNetworkPreferences.
-     * obj = OemNetworkPreferences
+     * Used internally when setting the default networks for OemNetworkPreferences.
+     * obj = Pair<OemNetworkPreferences, listener>
      */
     private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
 
@@ -568,6 +590,12 @@
     private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49;
 
     /**
+     * Used internally when setting a network preference for a user profile.
+     * obj = Pair<ProfileNetworkPreference, Listener>
+     */
+    private static final int EVENT_SET_PROFILE_NETWORK_PREFERENCE = 50;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -616,11 +644,8 @@
 
     private UserManager mUserManager;
 
-    private NetworkConfig[] mNetConfigs;
-    private int mNetworksDefined;
-
     // the set of network types that can only be enabled by system/sig apps
-    private List mProtectedNetworks;
+    private List<Integer> mProtectedNetworks;
 
     private Set<String> mWolSupportedInterfaces;
 
@@ -710,18 +735,63 @@
          *    They are therefore not thread-safe with respect to each other.
          *  - getNetworkForType() can be called at any time on binder threads. It is synchronized
          *    on mTypeLists to be thread-safe with respect to a concurrent remove call.
+         *  - getRestoreTimerForType(type) is also synchronized on mTypeLists.
          *  - dump is thread-safe with respect to concurrent add and remove calls.
          */
         private final ArrayList<NetworkAgentInfo> mTypeLists[];
         @NonNull
         private final ConnectivityService mService;
 
+        // Restore timers for requestNetworkForFeature (network type -> timer in ms). Types without
+        // an entry have no timer (equivalent to -1). Lazily loaded.
+        @NonNull
+        private ArrayMap<Integer, Integer> mRestoreTimers = new ArrayMap<>();
+
         LegacyTypeTracker(@NonNull ConnectivityService service) {
             mService = service;
             mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
         }
 
-        public void addSupportedType(int type) {
+        public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
+            final PackageManager pm = ctx.getPackageManager();
+            if (pm.hasSystemFeature(FEATURE_WIFI)) {
+                addSupportedType(TYPE_WIFI);
+            }
+            if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
+                addSupportedType(TYPE_WIFI_P2P);
+            }
+            if (tm.isDataCapable()) {
+                // Telephony does not have granular support for these types: they are either all
+                // supported, or none is supported
+                addSupportedType(TYPE_MOBILE);
+                addSupportedType(TYPE_MOBILE_MMS);
+                addSupportedType(TYPE_MOBILE_SUPL);
+                addSupportedType(TYPE_MOBILE_DUN);
+                addSupportedType(TYPE_MOBILE_HIPRI);
+                addSupportedType(TYPE_MOBILE_FOTA);
+                addSupportedType(TYPE_MOBILE_IMS);
+                addSupportedType(TYPE_MOBILE_CBS);
+                addSupportedType(TYPE_MOBILE_IA);
+                addSupportedType(TYPE_MOBILE_EMERGENCY);
+            }
+            if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
+                addSupportedType(TYPE_BLUETOOTH);
+            }
+            if (pm.hasSystemFeature(FEATURE_WATCH)) {
+                // TYPE_PROXY is only used on Wear
+                addSupportedType(TYPE_PROXY);
+            }
+            // Ethernet is often not specified in the configs, although many devices can use it via
+            // USB host adapters. Add it as long as the ethernet service is here.
+            if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) {
+                addSupportedType(TYPE_ETHERNET);
+            }
+
+            // Always add TYPE_VPN as a supported type
+            addSupportedType(TYPE_VPN);
+        }
+
+        private void addSupportedType(int type) {
             if (mTypeLists[type] != null) {
                 throw new IllegalStateException(
                         "legacy list for type " + type + "already initialized");
@@ -742,6 +812,35 @@
             return null;
         }
 
+        public int getRestoreTimerForType(int type) {
+            synchronized (mTypeLists) {
+                if (mRestoreTimers == null) {
+                    mRestoreTimers = loadRestoreTimers();
+                }
+                return mRestoreTimers.getOrDefault(type, -1);
+            }
+        }
+
+        private ArrayMap<Integer, Integer> loadRestoreTimers() {
+            final String[] configs = mService.mResources.get().getStringArray(
+                    com.android.connectivity.resources.R.array
+                            .config_legacy_networktype_restore_timers);
+            final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length);
+            for (final String config : configs) {
+                final String[] splits = TextUtils.split(config, ",");
+                if (splits.length != 2) {
+                    logwtf("Invalid restore timer token count: " + config);
+                    continue;
+                }
+                try {
+                    ret.put(Integer.parseInt(splits[0]), Integer.parseInt(splits[1]));
+                } catch (NumberFormatException e) {
+                    logwtf("Invalid restore timer number format: " + config, e);
+                }
+            }
+            return ret;
+        }
+
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
                 boolean isDefaultNetwork) {
             if (DBG) {
@@ -1164,64 +1263,12 @@
         mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
-        mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
-
-        // TODO: What is the "correct" way to do determine if this is a wifi only device?
-        boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false);
-        log("wifiOnly=" + wifiOnly);
-        String[] naStrings = context.getResources().getStringArray(
-                com.android.internal.R.array.networkAttributes);
-        for (String naString : naStrings) {
-            try {
-                NetworkConfig n = new NetworkConfig(naString);
-                if (VDBG) log("naString=" + naString + " config=" + n);
-                if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
-                    loge("Error in networkAttributes - ignoring attempt to define type " +
-                            n.type);
-                    continue;
-                }
-                if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
-                    log("networkAttributes - ignoring mobile as this dev is wifiOnly " +
-                            n.type);
-                    continue;
-                }
-                if (mNetConfigs[n.type] != null) {
-                    loge("Error in networkAttributes - ignoring attempt to redefine type " +
-                            n.type);
-                    continue;
-                }
-                mLegacyTypeTracker.addSupportedType(n.type);
-
-                mNetConfigs[n.type] = n;
-                mNetworksDefined++;
-            } catch(Exception e) {
-                // ignore it - leave the entry null
-            }
-        }
-
-        // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config.
-        if (mNetConfigs[TYPE_VPN] == null) {
-            // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we
-            // don't need to add TYPE_VPN to mNetConfigs.
-            mLegacyTypeTracker.addSupportedType(TYPE_VPN);
-            mNetworksDefined++;  // used only in the log() statement below.
-        }
-
-        // Do the same for Ethernet, since it's often not specified in the configs, although many
-        // devices can use it via USB host adapters.
-        if (mNetConfigs[TYPE_ETHERNET] == null
-                && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
-            mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
-            mNetworksDefined++;
-        }
-
-        if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
-
-        mProtectedNetworks = new ArrayList<Integer>();
+        mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager);
+        mProtectedNetworks = new ArrayList<>();
         int[] protectedNetworks = context.getResources().getIntArray(
                 com.android.internal.R.array.config_protectedNetworks);
         for (int p : protectedNetworks) {
-            if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+            if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) {
                 mProtectedNetworks.add(p);
             } else {
                 if (DBG) loge("Ignoring protectedNetwork " + p);
@@ -1273,20 +1320,31 @@
         mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
 
+        // This NAI is a sentinel used to offer no service to apps that are on a multi-layer
+        // request that doesn't allow fallback to the default network. It should never be visible
+        // to apps. As such, it's not in the list of NAIs and doesn't need many of the normal
+        // arguments like the handler or the DnsResolver.
+        // TODO : remove this ; it is probably better handled with a sentinel request.
         mNoServiceNetwork = new NetworkAgentInfo(null,
                 new Network(NO_SERVICE_NET_ID),
                 new NetworkInfo(TYPE_NONE, 0, "", ""),
-                new LinkProperties(), new NetworkCapabilities(), 0, mContext,
-                null, new NetworkAgentConfig(), this, null,
-                null, 0, INVALID_UID, mQosCallbackTracker, mDeps);
+                new LinkProperties(), new NetworkCapabilities(),
+                new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
+                new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker,
+                mDeps);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
+        return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
+    }
+
+    private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
+            @NonNull final UidRange uids) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
         netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
-        netCap.setSingleUid(uid);
+        netCap.setUids(Collections.singleton(uids));
         return netCap;
     }
 
@@ -2596,13 +2654,6 @@
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Can't set TCP buffer sizes:" + e);
         }
-
-        final Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.TCP_DEFAULT_INIT_RWND,
-                    mSystemProperties.getInt("net.tcp.default_init_rwnd", 0));
-        if (rwndValue != 0) {
-            mSystemProperties.setTcpInitRwnd(rwndValue);
-        }
     }
 
     @Override
@@ -2619,9 +2670,8 @@
         // if the system property isn't set, use the value for the apn type
         int ret = RESTORE_DEFAULT_NETWORK_DELAY;
 
-        if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) &&
-                (mNetConfigs[networkType] != null)) {
-            ret = mNetConfigs[networkType].restoreTime;
+        if (mLegacyTypeTracker.isTypeSupported(networkType)) {
+            ret = mLegacyTypeTracker.getRestoreTimerForType(networkType);
         }
         return ret;
     }
@@ -2949,7 +2999,7 @@
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
-                    updateNetworkScore(nai, msg.arg1);
+                    updateNetworkScore(nai, (NetworkScore) arg.second);
                     break;
                 }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
@@ -3656,6 +3706,7 @@
             mNetworkRequestInfoLogs.log("REGISTER " + nri);
             for (final NetworkRequest req : nri.mRequests) {
                 mNetworkRequests.put(req, nri);
+                // TODO: Consider update signal strength for other types.
                 if (req.isListen()) {
                     for (final NetworkAgentInfo network : mNetworkAgentInfos) {
                         if (req.networkCapabilities.hasSignalStrength()
@@ -3748,18 +3799,19 @@
         // listen requests won't keep up a network satisfying it. If this is not a multilayer
         // request, return immediately. For multilayer requests, check to see if any of the
         // multilayer requests may have a potential satisfier.
-        if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+        if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
+                || nri.mRequests.get(0).isListenForBest())) {
             return false;
         }
         for (final NetworkRequest req : nri.mRequests) {
             // This multilayer listen request is satisfied therefore no further requests need to be
             // evaluated deeming this network not a potential satisfier.
-            if (req.isListen() && nri.getActiveRequest() == req) {
+            if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) {
                 return false;
             }
             // As non-multilayer listen requests have already returned, the below would only happen
             // for a multilayer request therefore continue to the next request if available.
-            if (req.isListen()) {
+            if (req.isListen() || req.isListenForBest()) {
                 continue;
             }
             // If this Network is already the highest scoring Network for a request, or if
@@ -3799,8 +3851,7 @@
                 ? mNetworkRequests.get(request) : getNriForAppRequest(request);
 
         if (nri != null) {
-            if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
-                    && nri.mUid != callingUid) {
+            if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
                 log(String.format("UID %d attempted to %s for unowned request %s",
                         callingUid, requestedOperation, nri));
                 return null;
@@ -4524,12 +4575,17 @@
                     handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
                     break;
                 case EVENT_SET_OEM_NETWORK_PREFERENCE: {
-                    final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
-                            (Pair<OemNetworkPreferences,
-                                    IOnSetOemNetworkPreferenceListener>) msg.obj;
+                    final Pair<OemNetworkPreferences, IOnCompleteListener> arg =
+                            (Pair<OemNetworkPreferences, IOnCompleteListener>) msg.obj;
                     handleSetOemNetworkPreference(arg.first, arg.second);
                     break;
                 }
+                case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
+                    final Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener> arg =
+                            (Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
+                                    msg.obj;
+                    handleSetProfileNetworkPreference(arg.first, arg.second);
+                }
                 case EVENT_REPORT_NETWORK_ACTIVITY:
                     mNetworkActivityTracker.handleReportNetworkActivity();
                     break;
@@ -4828,6 +4884,10 @@
         Log.wtf(TAG, s);
     }
 
+    private static void logwtf(String s, Throwable t) {
+        Log.wtf(TAG, s, t);
+    }
+
     private static void loge(String s) {
         Log.e(TAG, s);
     }
@@ -5100,6 +5160,9 @@
 
     private void onUserRemoved(UserHandle user) {
         mPermissionMonitor.onUserRemoved(user);
+        // If there was a network preference for this user, remove it.
+        handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
+                null /* listener */);
         if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
             handleSetOemNetworkPreference(mOemNetworkPreferences, null);
         }
@@ -5543,8 +5606,10 @@
                 //  request if the app changes network state. http://b/29964605
                 enforceMeteredApnPolicy(networkCapabilities);
                 break;
-            case TRACK_BEST:
-                throw new UnsupportedOperationException("Not implemented yet");
+            case LISTEN_FOR_BEST:
+                enforceAccessPermission();
+                networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                break;
             default:
                 throw new IllegalArgumentException("Unsupported request type " + reqType);
         }
@@ -5552,11 +5617,17 @@
         ensureSufficientPermissionsForRequest(networkCapabilities,
                 Binder.getCallingPid(), callingUid, callingPackageName);
 
-        // Set the UID range for this request to the single UID of the requester, or to an empty
-        // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
+        // Enforce FOREGROUND if the caller does not have permission to use background network.
+        if (reqType == LISTEN_FOR_BEST) {
+            restrictBackgroundRequestForCaller(networkCapabilities);
+        }
+
+        // Set the UID range for this request to the single UID of the requester, unless the
+        // requester has the permission to specify other UIDs.
         // This will overwrite any allowed UIDs in the requested capabilities. Though there
         // are no visible methods to set the UIDs, an app could use reflection to try and get
         // networks for other apps so it's essential that the UIDs are overwritten.
+        // Also set the requester UID and package name in the request.
         restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
                 callingUid, callingPackageName);
 
@@ -5890,10 +5961,16 @@
     @GuardedBy("mBlockedAppUids")
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
-    // Current OEM network preferences.
+    // Current OEM network preferences. This object must only be written to on the handler thread.
+    // Since it is immutable and always non-null, other threads may read it if they only care
+    // about seeing a consistent object but not that it is current.
     @NonNull
     private OemNetworkPreferences mOemNetworkPreferences =
             new OemNetworkPreferences.Builder().build();
+    // Current per-profile network preferences. This object follows the same threading rules as
+    // the OEM network preferences above.
+    @NonNull
+    private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
 
     // The always-on request for an Internet-capable network that apps without a specific default
     // fall back to.
@@ -6083,20 +6160,6 @@
         return nai == getDefaultNetwork();
     }
 
-    // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
-    // changes that would conflict throughout the automerger graph. Having this method temporarily
-    // helps with the process of going through with all these dependent changes across the entire
-    // tree.
-    /**
-     * Register a new agent. {@see #registerNetworkAgent} below.
-     */
-    public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
-            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig) {
-        return registerNetworkAgent(na, networkInfo, linkProperties, networkCapabilities,
-                currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
-    }
-
     /**
      * Register a new agent with ConnectivityService to handle a network.
      *
@@ -6107,7 +6170,7 @@
      *         later : see {@link #updateLinkProperties}.
      * @param networkCapabilities the initial capabilites of this network. They can be updated
      *         later : see {@link #updateCapabilities}.
-     * @param currentScore the initial score of the network. See
+     * @param initialScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
      * @param networkAgentConfig metadata about the network. This is never updated.
      * @param providerId the ID of the provider owning this NetworkAgent.
@@ -6115,10 +6178,12 @@
      */
     public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+            @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig,
+            int providerId) {
         Objects.requireNonNull(networkInfo, "networkInfo must not be null");
         Objects.requireNonNull(linkProperties, "linkProperties must not be null");
         Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+        Objects.requireNonNull(initialScore, "initialScore must not be null");
         Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null");
         if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
             enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
@@ -6130,7 +6195,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             return registerNetworkAgentInternal(na, networkInfo, linkProperties,
-                    networkCapabilities, currentScore, networkAgentConfig, providerId, uid);
+                    networkCapabilities, initialScore, networkAgentConfig, providerId, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -6138,7 +6203,8 @@
 
     private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) {
+            NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId,
+            int uid) {
         if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
             // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
             // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
@@ -7862,7 +7928,7 @@
         }
     }
 
-    private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) {
+    private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final NetworkScore score) {
         if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
         nai.setScore(score);
         rematchAllNetworksAndRequests();
@@ -8179,7 +8245,7 @@
         }
 
         Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
+                ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null);
     }
 
     @Override
@@ -8884,13 +8950,13 @@
         private int transportTypeToLegacyType(int type) {
             switch (type) {
                 case NetworkCapabilities.TRANSPORT_CELLULAR:
-                    return ConnectivityManager.TYPE_MOBILE;
+                    return TYPE_MOBILE;
                 case NetworkCapabilities.TRANSPORT_WIFI:
-                    return ConnectivityManager.TYPE_WIFI;
+                    return TYPE_WIFI;
                 case NetworkCapabilities.TRANSPORT_BLUETOOTH:
-                    return ConnectivityManager.TYPE_BLUETOOTH;
+                    return TYPE_BLUETOOTH;
                 case NetworkCapabilities.TRANSPORT_ETHERNET:
-                    return ConnectivityManager.TYPE_ETHERNET;
+                    return TYPE_ETHERNET;
                 default:
                     loge("Unexpected transport in transportTypeToLegacyType: " + type);
             }
@@ -9105,6 +9171,143 @@
         mQosCallbackTracker.unregisterCallback(callback);
     }
 
+    // Network preference per-profile and OEM network preferences can't be set at the same
+    // time, because it is unclear what should happen if both preferences are active for
+    // one given UID. To make it possible, the stack would have to clarify what would happen
+    // in case both are active at the same time. The implementation may have to be adjusted
+    // to implement the resulting rules. For example, a priority could be defined between them,
+    // where the OEM preference would be considered less or more important than the enterprise
+    // preference ; this would entail implementing the priorities somehow, e.g. by doing
+    // UID arithmetic with UID ranges or passing a priority to netd so that the routing rules
+    // are set at the right level. Other solutions are possible, e.g. merging of the
+    // preferences for the relevant UIDs.
+    private static void throwConcurrentPreferenceException() {
+        throw new IllegalStateException("Can't set NetworkPreferenceForUser and "
+                + "set OemNetworkPreference at the same time");
+    }
+
+    /**
+     * Request that a user profile is put by default on a network matching a given preference.
+     *
+     * See the documentation for the individual preferences for a description of the supported
+     * behaviors.
+     *
+     * @param profile the profile concerned.
+     * @param preference the preference for this profile, as one of the PROFILE_NETWORK_PREFERENCE_*
+     *                   constants.
+     * @param listener an optional listener to listen for completion of the operation.
+     */
+    @Override
+    public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+            @ConnectivityManager.ProfileNetworkPreference final int preference,
+            @Nullable final IOnCompleteListener listener) {
+        Objects.requireNonNull(profile);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        if (DBG) {
+            log("setProfileNetworkPreference " + profile + " to " + preference);
+        }
+        if (profile.getIdentifier() < 0) {
+            throw new IllegalArgumentException("Must explicitly specify a user handle ("
+                    + "UserHandle.CURRENT not supported)");
+        }
+        final UserManager um;
+        try {
+            um = mContext.createContextAsUser(profile, 0 /* flags */)
+                    .getSystemService(UserManager.class);
+        } catch (IllegalStateException e) {
+            throw new IllegalArgumentException("Profile does not exist");
+        }
+        if (!um.isManagedProfile()) {
+            throw new IllegalArgumentException("Profile must be a managed profile");
+        }
+        // Strictly speaking, mOemNetworkPreferences should only be touched on the
+        // handler thread. However it is an immutable object, so reading the reference is
+        // safe - it's just possible the value is slightly outdated. For the final check,
+        // see #handleSetProfileNetworkPreference. But if this can be caught here it is a
+        // lot easier to understand, so opportunistically check it.
+        if (!mOemNetworkPreferences.isEmpty()) {
+            throwConcurrentPreferenceException();
+        }
+        final NetworkCapabilities nc;
+        switch (preference) {
+            case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
+                nc = null;
+                break;
+            case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+                final UidRange uids = UidRange.createForUser(profile);
+                nc = createDefaultNetworkCapabilitiesForUidRange(uids);
+                nc.addCapability(NET_CAPABILITY_ENTERPRISE);
+                nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid preference in setProfileNetworkPreference");
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
+                new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener)));
+    }
+
+    private void validateNetworkCapabilitiesOfProfileNetworkPreference(
+            @Nullable final NetworkCapabilities nc) {
+        if (null == nc) return; // Null caps are always allowed. It means to remove the setting.
+        ensureRequestableCapabilities(nc);
+    }
+
+    private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
+            @NonNull final ProfileNetworkPreferences prefs) {
+        final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
+        for (final ProfileNetworkPreferences.Preference pref : prefs.preferences) {
+            // The NRI for a user should be comprised of two layers:
+            // - The request for the capabilities
+            // - The request for the default network, for fallback. Create an image of it to
+            //   have the correct UIDs in it (also a request can only be part of one NRI, because
+            //   of lookups in 1:1 associations like mNetworkRequests).
+            // Note that denying a fallback can be implemented simply by not adding the second
+            // request.
+            final ArrayList<NetworkRequest> nrs = new ArrayList<>();
+            nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities));
+            nrs.add(createDefaultRequest());
+            setNetworkRequestUids(nrs, pref.capabilities.getUids());
+            final NetworkRequestInfo nri = new NetworkRequestInfo(nrs);
+            result.add(nri);
+        }
+        return result;
+    }
+
+    private void handleSetProfileNetworkPreference(
+            @NonNull final ProfileNetworkPreferences.Preference preference,
+            @Nullable final IOnCompleteListener listener) {
+        // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+        // particular because it's not clear what preference should win in case both apply
+        // to the same app.
+        // The binder call has already checked this, but as mOemNetworkPreferences is only
+        // touched on the handler thread, it's theoretically not impossible that it has changed
+        // since.
+        if (!mOemNetworkPreferences.isEmpty()) {
+            // This may happen on a device with an OEM preference set when a user is removed.
+            // In this case, it's safe to ignore. In particular this happens in the tests.
+            loge("handleSetProfileNetworkPreference, but OEM network preferences not empty");
+            return;
+        }
+
+        validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
+
+        mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
+        final ArraySet<NetworkRequestInfo> nris =
+                createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
+        replaceDefaultNetworkRequestsForPreference(nris);
+        // Finally, rematch.
+        rematchAllNetworksAndRequests();
+
+        if (null != listener) {
+            try {
+                listener.onComplete();
+            } catch (RemoteException e) {
+                loge("Listener for setProfileNetworkPreference has died");
+            }
+        }
+    }
+
     private void enforceAutomotiveDevice() {
         final boolean isAutomotiveDevice =
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
@@ -9123,17 +9326,26 @@
      * Calling this will overwrite the existing preference.
      *
      * @param preference {@link OemNetworkPreferences} The application network preference to be set.
-     * @param listener {@link ConnectivityManager.OnSetOemNetworkPreferenceListener} Listener used
+     * @param listener {@link ConnectivityManager.OnCompleteListener} Listener used
      * to communicate completion of setOemNetworkPreference();
      */
     @Override
     public void setOemNetworkPreference(
             @NonNull final OemNetworkPreferences preference,
-            @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+            @Nullable final IOnCompleteListener listener) {
 
         enforceAutomotiveDevice();
         enforceOemNetworkPreferencesPermission();
 
+        if (!mProfileNetworkPreferences.isEmpty()) {
+            // Strictly speaking, mProfileNetworkPreferences should only be touched on the
+            // handler thread. However it is an immutable object, so reading the reference is
+            // safe - it's just possible the value is slightly outdated. For the final check,
+            // see #handleSetOemPreference. But if this can be caught here it is a
+            // lot easier to understand, so opportunistically check it.
+            throwConcurrentPreferenceException();
+        }
+
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         validateOemNetworkPreferences(preference);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
@@ -9152,11 +9364,22 @@
 
     private void handleSetOemNetworkPreference(
             @NonNull final OemNetworkPreferences preference,
-            @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+            @Nullable final IOnCompleteListener listener) {
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         if (DBG) {
             log("set OEM network preferences :" + preference.toString());
         }
+        // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+        // particular because it's not clear what preference should win in case both apply
+        // to the same app.
+        // The binder call has already checked this, but as mOemNetworkPreferences is only
+        // touched on the handler thread, it's theoretically not impossible that it has changed
+        // since.
+        if (!mProfileNetworkPreferences.isEmpty()) {
+            logwtf("handleSetOemPreference, but per-profile network preferences not empty");
+            return;
+        }
+
         final ArraySet<NetworkRequestInfo> nris =
                 new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
         replaceDefaultNetworkRequestsForPreference(nris);
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index d10cf4d..fa3771a 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -104,6 +105,9 @@
 public class PersistentDataBlockService extends SystemService {
     private static final String TAG = PersistentDataBlockService.class.getSimpleName();
 
+    private static final String GSI_SANDBOX = "/data/gsi_persistent_data";
+    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
     private static final int HEADER_SIZE = 8;
     // Magic number to mark block device as adhering to the format consumed by this service
@@ -128,12 +132,13 @@
     private static final String FLASH_LOCK_UNLOCKED = "0";
 
     private final Context mContext;
-    private final String mDataBlockFile;
+    private final boolean mIsRunningDSU;
     private final Object mLock = new Object();
     private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
 
     private int mAllowedUid = -1;
     private long mBlockDeviceSize;
+    private String mDataBlockFile;
 
     @GuardedBy("mLock")
     private boolean mIsWritable = true;
@@ -142,6 +147,7 @@
         super(context);
         mContext = context;
         mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+        mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
         mBlockDeviceSize = -1; // Load lazily
     }
 
@@ -286,14 +292,28 @@
         return true;
     }
 
+    private FileOutputStream getBlockOutputStream() throws IOException {
+        if (!mIsRunningDSU) {
+            return new FileOutputStream(new File(mDataBlockFile));
+        } else {
+            File sandbox = new File(GSI_SANDBOX);
+            File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
+            if (!sandbox.exists()) {
+                FileUtils.copy(realpdb, sandbox);
+                mDataBlockFile = GSI_SANDBOX;
+            }
+            Slog.i(TAG, "PersistentDataBlock copy-on-write");
+            return new FileOutputStream(sandbox);
+        }
+    }
+
     private boolean computeAndWriteDigestLocked() {
         byte[] digest = computeDigestLocked(null);
         if (digest != null) {
             DataOutputStream outputStream;
             try {
-                outputStream = new DataOutputStream(
-                        new FileOutputStream(new File(mDataBlockFile)));
-            } catch (FileNotFoundException e) {
+                outputStream = new DataOutputStream(getBlockOutputStream());
+            } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return false;
             }
@@ -358,8 +378,8 @@
     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
         DataOutputStream outputStream;
         try {
-            outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
-        } catch (FileNotFoundException e) {
+            outputStream = new DataOutputStream(getBlockOutputStream());
+        } catch (IOException e) {
             Slog.e(TAG, "partition not available?", e);
             return;
         }
@@ -384,8 +404,8 @@
     private void doSetOemUnlockEnabledLocked(boolean enabled) {
         FileOutputStream outputStream;
         try {
-            outputStream = new FileOutputStream(new File(mDataBlockFile));
-        } catch (FileNotFoundException e) {
+            outputStream = getBlockOutputStream();
+        } catch (IOException e) {
             Slog.e(TAG, "partition not available", e);
             return;
         }
@@ -461,8 +481,8 @@
 
             DataOutputStream outputStream;
             try {
-                outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
-            } catch (FileNotFoundException e) {
+                outputStream = new DataOutputStream(getBlockOutputStream());
+            } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return -1;
             }
@@ -547,6 +567,17 @@
         public void wipe() {
             enforceOemUnlockWritePermission();
 
+            if (mIsRunningDSU) {
+                File sandbox = new File(GSI_SANDBOX);
+                if (sandbox.exists()) {
+                    if (sandbox.delete()) {
+                        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+                    } else {
+                        Slog.e(TAG, "Failed to wipe sandbox persistent data block");
+                    }
+                }
+                return;
+            }
             synchronized (mLock) {
                 int ret = nativeWipe(mDataBlockFile);
 
@@ -706,8 +737,8 @@
         private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
             FileOutputStream outputStream;
             try {
-                outputStream = new FileOutputStream(new File(mDataBlockFile));
-            } catch (FileNotFoundException e) {
+                outputStream = getBlockOutputStream();
+            } catch (IOException e) {
                 Slog.e(TAG, "partition not available", e);
                 return;
             }
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 140f24f..051cd99 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -64,7 +64,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
 import com.android.server.vcn.VcnContext;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index e2086b0..e74c936 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -589,9 +589,9 @@
             Slog.w(TAG, "exception reading modem stats: " + e.getCause());
         }
 
-        final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDelta;
+        final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas;
         if (mMeasuredEnergySnapshot == null || futureECRs == null) {
-            measuredEnergyDelta = null;
+            measuredEnergyDeltas = null;
         } else {
             final int voltageMv;
             synchronized (mStats) {
@@ -610,7 +610,7 @@
                 ecrs = null;
             }
 
-            measuredEnergyDelta = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv);
+            measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv);
         }
 
         final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -633,10 +633,10 @@
                 }
 
                 final long[] cpuClusterChargeUC;
-                if (measuredEnergyDelta == null) {
+                if (measuredEnergyDeltas == null) {
                     cpuClusterChargeUC = null;
                 } else {
-                    cpuClusterChargeUC = measuredEnergyDelta.cpuClusterChargeUC;
+                    cpuClusterChargeUC = measuredEnergyDeltas.cpuClusterChargeUC;
                 }
                 mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC);
             }
@@ -650,9 +650,9 @@
                 mStats.updateRpmStatsLocked(elapsedRealtimeUs);
             }
 
-            // Inform mStats about each applicable measured energy.
-            if (measuredEnergyDelta != null) {
-                final long displayChargeUC = measuredEnergyDelta.displayChargeUC;
+            // Inform mStats about each applicable measured energy (unless addressed elsewhere).
+            if (measuredEnergyDeltas != null) {
+                final long displayChargeUC = measuredEnergyDeltas.displayChargeUC;
                 if (displayChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
                     // If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
                     mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState,
@@ -660,19 +660,23 @@
                 }
             }
             // Inform mStats about each applicable custom energy bucket.
-            if (measuredEnergyDelta != null
-                    && measuredEnergyDelta.otherTotalChargeUC != null) {
+            if (measuredEnergyDeltas != null
+                    && measuredEnergyDeltas.otherTotalChargeUC != null) {
                 // Iterate over the custom (EnergyConsumerType.OTHER) ordinals.
-                for (int ord = 0; ord < measuredEnergyDelta.otherTotalChargeUC.length; ord++) {
-                    long totalEnergy = measuredEnergyDelta.otherTotalChargeUC[ord];
-                    SparseLongArray uidEnergies = measuredEnergyDelta.otherUidChargesUC[ord];
+                for (int ord = 0; ord < measuredEnergyDeltas.otherTotalChargeUC.length; ord++) {
+                    long totalEnergy = measuredEnergyDeltas.otherTotalChargeUC[ord];
+                    SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidChargesUC[ord];
                     mStats.updateCustomMeasuredEnergyStatsLocked(ord, totalEnergy, uidEnergies);
                 }
             }
 
             if (bluetoothInfo != null) {
                 if (bluetoothInfo.isValid()) {
-                    mStats.updateBluetoothStateLocked(bluetoothInfo, elapsedRealtime, uptime);
+                    final long btChargeUC = measuredEnergyDeltas != null
+                            ? measuredEnergyDeltas.bluetoothChargeUC
+                            : MeasuredEnergySnapshot.UNAVAILABLE;
+                    mStats.updateBluetoothStateLocked(bluetoothInfo,
+                            btChargeUC, elapsedRealtime, uptime);
                 } else {
                     Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
                 }
@@ -684,10 +688,10 @@
 
         if (wifiInfo != null) {
             if (wifiInfo.isValid()) {
-                // TODO: wifiEnergyDelta = measuredEnergyDelta.consumerTypeEnergyUJ
-                //               .get(EnergyConsumerType.WIFI, MeasuredEnergySnapshot.UNAVAILABLE)
-                mStats.updateWifiState(extractDeltaLocked(wifiInfo)
-                        /*, TODO: wifiEnergyDelta */, elapsedRealtime, uptime);
+                final long wifiChargeUC = measuredEnergyDeltas != null ?
+                        measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+                mStats.updateWifiState(
+                        extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
             } else {
                 Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
             }
@@ -820,13 +824,19 @@
         for (int idx = 0; idx < size; idx++) {
             final EnergyConsumer consumer = idToConsumer.valueAt(idx);
             switch (consumer.type) {
+                case EnergyConsumerType.BLUETOOTH:
+                    buckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH] = true;
+                    break;
+                case EnergyConsumerType.CPU_CLUSTER:
+                    buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+                    break;
                 case EnergyConsumerType.DISPLAY:
                     buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true;
                     buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true;
                     buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER] = true;
                     break;
-                case EnergyConsumerType.CPU_CLUSTER:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+                case EnergyConsumerType.WIFI:
+                    buckets[MeasuredEnergyStats.POWER_BUCKET_WIFI] = true;
                     break;
             }
         }
@@ -864,13 +874,18 @@
         }
 
         final IntArray energyConsumerIds = new IntArray();
+        if ((flags & UPDATE_BT) != 0) {
+            addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.BLUETOOTH);
+        }
         if ((flags & UPDATE_CPU) != 0) {
             addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER);
         }
         if ((flags & UPDATE_DISPLAY) != 0) {
             addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
         }
-        // TODO: Wifi, Bluetooth, etc., go here
+        if ((flags & UPDATE_WIFI) != 0) {
+            addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI);
+        }
 
         if (energyConsumerIds.size() == 0) {
             return null;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6b9fc07..c3f97ad 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
 
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -1927,7 +1928,7 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
-                mStats.updateWifiState(info, elapsedRealtime, uptime);
+                mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
             });
         }
     }
@@ -1945,7 +1946,8 @@
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
                 synchronized (mStats) {
-                    mStats.updateBluetoothStateLocked(info, elapsedRealtime, uptime);
+                    mStats.updateBluetoothStateLocked(
+                            info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index 9b2ca13..4c9ab63 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -41,7 +41,7 @@
 
     private static final int MILLIVOLTS_PER_VOLT = 1000;
 
-    public static final long UNAVAILABLE = -1L;
+    public static final long UNAVAILABLE = android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
 
     /** Map of {@link EnergyConsumer#id} to its corresponding {@link EnergyConsumer}. */
     private final SparseArray<EnergyConsumer> mEnergyConsumers;
@@ -109,12 +109,18 @@
 
     /** Class for returning the relevant data calculated from the measured energy delta */
     static class MeasuredEnergyDeltaData {
-        /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
-        public long displayChargeUC = UNAVAILABLE;
+        /** The chargeUC for {@link EnergyConsumerType#BLUETOOTH}. */
+        public long bluetoothChargeUC = UNAVAILABLE;
 
         /** The chargeUC for {@link EnergyConsumerType#CPU_CLUSTER}s. */
         public long[] cpuClusterChargeUC = null;
 
+        /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
+        public long displayChargeUC = UNAVAILABLE;
+
+        /** The chargeUC for {@link EnergyConsumerType#WIFI}. */
+        public long wifiChargeUC = UNAVAILABLE;
+
         /** Map of {@link EnergyConsumerType#OTHER} ordinals to their total chargeUC. */
         public @Nullable long[] otherTotalChargeUC = null;
 
@@ -196,8 +202,8 @@
 
             final long deltaChargeUC = calculateChargeConsumedUC(deltaUJ, avgVoltageMV);
             switch (type) {
-                case EnergyConsumerType.DISPLAY:
-                    output.displayChargeUC = deltaChargeUC;
+                case EnergyConsumerType.BLUETOOTH:
+                    output.bluetoothChargeUC = deltaChargeUC;
                     break;
 
                 case EnergyConsumerType.CPU_CLUSTER:
@@ -207,6 +213,14 @@
                     output.cpuClusterChargeUC[ordinal] = deltaChargeUC;
                     break;
 
+                case EnergyConsumerType.DISPLAY:
+                    output.displayChargeUC = deltaChargeUC;
+                    break;
+
+                case EnergyConsumerType.WIFI:
+                    output.wifiChargeUC = deltaChargeUC;
+                    break;
+
                 case EnergyConsumerType.OTHER:
                     if (output.otherTotalChargeUC == null) {
                         output.otherTotalChargeUC = new long[getNumOtherOrdinals()];
@@ -215,6 +229,7 @@
                     output.otherTotalChargeUC[ordinal] = deltaChargeUC;
                     output.otherUidChargesUC[ordinal] = otherUidCharges;
                     break;
+
                 default:
                     Slog.w(TAG, "Ignoring consumer " + consumer.name + " of unknown type " + type);
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1122f7f..109ffe3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3091,14 +3091,10 @@
             return AppOpsManager.MODE_IGNORED;
         }
 
-        // This is a workaround for R QPR, new API change is not allowed. We only allow the current
-        // voice recognizer is also the voice interactor to noteproxy op.
-        final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext,
-                proxyPackageName, code, UserHandle.getUserId(proxyUid));
         final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
         final boolean isProxyTrusted = mContext.checkPermission(
                 Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
-                == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame;
+                == PackageManager.PERMISSION_GRANTED || isSelfBlame;
 
         final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
@@ -3576,14 +3572,10 @@
             return AppOpsManager.MODE_IGNORED;
         }
 
-        // This is a workaround for R QPR, new API change is not allowed. We only allow the current
-        // voice recognizer is also the voice interactor to noteproxy op.
-        final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext,
-                proxyPackageName, code, UserHandle.getUserId(proxyUid));
         final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
         final boolean isProxyTrusted = mContext.checkPermission(
                 Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
-                == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame;
+                == PackageManager.PERMISSION_GRANTED || isSelfBlame;
 
         final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 71bac57..ce728bc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -62,6 +62,7 @@
         // Nothing to do here
     }
 
+    @Override
     public void start(@NonNull Callback callback) {
         super.start(callback);
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index ddcfcad..adffba2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -63,6 +63,12 @@
     }
 
     @Override
+    public void start(@NonNull Callback callback) {
+        super.start(callback);
+        startHalOperation();
+    }
+
+    @Override
     protected void startHalOperation() {
         try {
             getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5bf15dc..71565301 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -80,6 +80,7 @@
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
@@ -95,21 +96,20 @@
     private static final String PIPE_NAME = "pipe:clipboard";
     private static final String PIPE_DEVICE = "/dev/qemu_pipe";
 
+    private static byte[] createOpenHandshake() {
+        // String.getBytes doesn't include the null terminator,
+        // but the QEMU pipe device requires the pipe service name
+        // to be null-terminated.
+
+        final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+        bits[PIPE_NAME.length()] = 0;
+        return bits;
+    }
+
     private void openPipe() {
         try {
-            // String.getBytes doesn't include the null terminator,
-            // but the QEMU pipe device requires the pipe service name
-            // to be null-terminated.
-            byte[] b = new byte[PIPE_NAME.length() + 1];
-            b[PIPE_NAME.length()] = 0;
-            System.arraycopy(
-                PIPE_NAME.getBytes(),
-                0,
-                b,
-                0,
-                PIPE_NAME.length());
             mPipe = new RandomAccessFile(PIPE_DEVICE, "rw");
-            mPipe.write(b);
+            mPipe.write(createOpenHandshake());
         } catch (IOException e) {
             try {
                 if (mPipe != null) mPipe.close();
@@ -173,7 +173,7 @@
 
     private static final String TAG = "ClipboardService";
     private static final boolean IS_EMULATOR =
-        SystemProperties.getBoolean("ro.kernel.qemu", false);
+            SystemProperties.getBoolean("ro.boot.qemu", false);
 
     // DeviceConfig properties
     private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index ef76734..a25b89a 100644
--- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -17,7 +17,6 @@
 package com.android.server.connectivity;
 
 import android.os.SystemProperties;
-import android.sysprop.NetworkProperties;
 
 public class MockableSystemProperties {
 
@@ -32,10 +31,4 @@
     public boolean getBoolean(String key, boolean def) {
         return SystemProperties.getBoolean(key, def);
     }
-    /**
-     * Set net.tcp_def_init_rwnd to the tcp initial receive window size.
-     */
-    public void setTcpInitRwnd(int value) {
-        NetworkProperties.tcp_init_rwnd(value);
-    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 803cc9d..e44dcf5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -35,6 +35,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkStateSnapshot;
 import android.net.QosCallbackException;
 import android.net.QosFilter;
@@ -302,8 +303,8 @@
     // validated).
     private boolean mInactive;
 
-    // This represents the quality of the network with no clear scale.
-    private int mScore;
+    // This represents the quality of the network.
+    private NetworkScore mScore;
 
     // The list of NetworkRequests being satisfied by this Network.
     private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -338,7 +339,8 @@
     private final QosCallbackTracker mQosCallbackTracker;
 
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
-            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
+            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
+            @NonNull NetworkScore score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
             QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
@@ -603,9 +605,9 @@
         }
 
         @Override
-        public void sendScore(int score) {
-            mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, score, 0,
-                    new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+        public void sendScore(@NonNull final NetworkScore score) {
+            mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED,
+                    new Pair<>(NetworkAgentInfo.this, score)).sendToTarget();
         }
 
         @Override
@@ -717,6 +719,7 @@
                 break;
 
             case LISTEN:
+            case LISTEN_FOR_BEST:
             case TRACK_DEFAULT:
             case TRACK_SYSTEM_DEFAULT:
                 break;
@@ -857,7 +860,7 @@
             return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
         }
 
-        int score = mScore;
+        int score = mScore.getLegacyInt();
         if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
             score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
         }
@@ -886,7 +889,7 @@
         return getCurrentScore(true);
     }
 
-    public void setScore(final int score) {
+    public void setScore(final NetworkScore score) {
         mScore = score;
     }
 
diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
new file mode 100644
index 0000000..dd2815d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.UserHandle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A data class containing all the per-profile network preferences.
+ *
+ * A given profile can only have one preference.
+ */
+public class ProfileNetworkPreferences {
+    /**
+     * A single preference, as it applies to a given user profile.
+     */
+    public static class Preference {
+        @NonNull public final UserHandle user;
+        // Capabilities are only null when sending an object to remove the setting for a user
+        @Nullable public final NetworkCapabilities capabilities;
+
+        public Preference(@NonNull final UserHandle user,
+                @Nullable final NetworkCapabilities capabilities) {
+            this.user = user;
+            this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
+        }
+
+        /** toString */
+        public String toString() {
+            return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]";
+        }
+    }
+
+    @NonNull public final List<Preference> preferences;
+
+    public ProfileNetworkPreferences() {
+        preferences = Collections.EMPTY_LIST;
+    }
+
+    private ProfileNetworkPreferences(@NonNull final List<Preference> list) {
+        preferences = Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Returns a new object consisting of this object plus the passed preference.
+     *
+     * If a preference already exists for the same user, it will be replaced by the passed
+     * preference. Passing a Preference object containing a null capabilities object is equivalent
+     * to (and indeed, implemented as) removing the preference for this user.
+     */
+    public ProfileNetworkPreferences plus(@NonNull final Preference pref) {
+        final ArrayList<Preference> newPrefs = new ArrayList<>();
+        for (final Preference existingPref : preferences) {
+            if (!existingPref.user.equals(pref.user)) {
+                newPrefs.add(existingPref);
+            }
+        }
+        if (null != pref.capabilities) {
+            newPrefs.add(pref);
+        }
+        return new ProfileNetworkPreferences(newPrefs);
+    }
+
+    public boolean isEmpty() {
+        return preferences.isEmpty();
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e9c3ec3..ffb532e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2923,8 +2923,7 @@
                 dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0);
                 break;
         }
-        mWindowManagerInternal.updateInputMethodWindowStatus(token,
-                (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
+        mWindowManagerInternal.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
     }
 
     @BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index b13c307..7e5e427 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -21,7 +21,6 @@
 import android.content.pm.PackageManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.Printer;
 import android.util.Slog;
 import android.view.inputmethod.InputMethodInfo;
@@ -502,7 +501,7 @@
     public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
         if (mController == null) {
             if (DEBUG) {
-                Log.e(TAG, "mController shouldn't be null.");
+                Slog.e(TAG, "mController shouldn't be null.");
             }
             return;
         }
@@ -520,7 +519,7 @@
             InputMethodSubtype subtype) {
         if (mController == null) {
             if (DEBUG) {
-                Log.e(TAG, "mController shouldn't be null.");
+                Slog.e(TAG, "mController shouldn't be null.");
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index e1c011d..c8c212b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -47,9 +47,11 @@
 import com.android.server.location.ClientBrokerProto;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
@@ -205,14 +207,19 @@
      * allowed to communicate over that channel. A channel is defined to have been opened if the
      * client has sent or received messages from the particular nanoapp.
      */
-    private final Map<Long, Integer> mMessageChannelNanoappIdMap =
-            new ConcurrentHashMap<Long, Integer>();
+    private final Map<Long, Integer> mMessageChannelNanoappIdMap = new ConcurrentHashMap<>();
+
+    /**
+     * Set containing all nanoapps that have been forcefully transitioned to the denied
+     * authorization state (via CLI) to ensure they don't transition back to the granted state
+     * later if, for example, a permission check is performed due to another nanoapp
+     */
+    private final Set<Long> mForceDeniedNapps = new HashSet<>();
 
     /**
      * Map containing all nanoapps that have active auth state denial timers.
      */
-    private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap =
-            new ConcurrentHashMap<Long, AuthStateDenialTimer>();
+    private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap = new ConcurrentHashMap<>();
 
     /**
      * Callback used to obtain the latest set of nanoapp permissions and verify this client has
@@ -637,7 +644,8 @@
     private int updateNanoAppAuthState(
             long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired) {
         return updateNanoAppAuthState(
-                nanoAppId, nanoappPermissions, gracePeriodExpired, false /* forceDenied */);
+                nanoAppId, nanoappPermissions, gracePeriodExpired,
+                mForceDeniedNapps.contains(nanoAppId) /* forceDenied */);
     }
 
     /**
@@ -679,6 +687,7 @@
             // any state -> DENIED if "forceDenied" is true
             if (forceDenied) {
                 newAuthState = AUTHORIZATION_DENIED;
+                mForceDeniedNapps.add(nanoAppId);
             } else if (gracePeriodExpired) {
                 if (curAuthState == AUTHORIZATION_DENIED_GRACE_PERIOD) {
                     newAuthState = AUTHORIZATION_DENIED;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d5a9e3c..e588366 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -10635,17 +10635,17 @@
                     return true;
                 }
             }
-            String toastMessage = "Indirect activity start from " + packageName;
             String logcatMessage =
                     "Indirect notification activity start (trampoline) from " + packageName;
-
+            // Call to toast() method is posted to mHandler below to offload PM lookup from the
+            // activity start path
             if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) {
-                toast(toastMessage + " blocked.");
+                mHandler.post(() -> toast(packageName, uid, /* blocked */ true));
                 Slog.e(TAG, logcatMessage + " blocked");
                 return false;
             } else {
                 if (mPackagesShown.add(packageName)) {
-                    toast(toastMessage + ". This will be blocked in S.");
+                    mHandler.post(() -> toast(packageName, uid, /* blocked */ false));
                 }
                 Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
                 return true;
@@ -10661,10 +10661,19 @@
                     && !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
         }
 
-        private void toast(String message) {
-            mUiHandler.post(() ->
-                    Toast.makeText(getUiContext(), message + "\nSee g.co/dev/trampolines.",
-                            Toast.LENGTH_LONG).show());
+        private void toast(String packageName, int uid, boolean blocked) {
+            final CharSequence label;
+            try {
+                label = mPackageManagerClient.getApplicationLabel(
+                        mPackageManager.getApplicationInfo(packageName, 0,
+                                UserHandle.getUserId(uid)));
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unexpected exception obtaining app label from PackageManager", e);
+                return;
+            }
+            mUiHandler.post(() -> Toast.makeText(getUiContext(),
+                    label + " launch " + (blocked ? "blocked" : "will be blocked")
+                            + "\ng.co/dev/trampolines", Toast.LENGTH_LONG).show());
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 4be509b..1d556fe 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1492,7 +1492,7 @@
                 }
                 return null;
             }
-            final ResolveInfo res = new ResolveInfo();
+            final ResolveInfo res = new ResolveInfo(info.hasCategory(Intent.CATEGORY_BROWSABLE));
             res.activityInfo = ai;
             if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dfe72b2..1530e41 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1768,6 +1768,11 @@
         public int[] getAllUserIds() {
             return mUserManager.getUserIds();
         }
+
+        @Override
+        public boolean doesUserExist(@UserIdInt int userId) {
+            return mUserManager.exists(userId);
+        }
     }
 
     /**
@@ -2639,8 +2644,7 @@
             // We'll want to include browser possibilities in a few cases
             boolean includeBrowser = false;
 
-            if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
-                            matchFlags)) {
+            if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
                 result.addAll(undefinedList);
                 // Maybe add one for the other profile.
                 if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
@@ -25693,6 +25697,7 @@
         if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) {
             mPermissionManager.onUserCreated(userId);
             mLegacyPermissionManager.grantDefaultPermissions(userId);
+            mDomainVerificationManager.clearUser(userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 3b77c39..0c8e36b 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -189,7 +189,7 @@
 
     private static int getDexMetadataType(String dexMetadataPath) {
         if (dexMetadataPath == null) {
-            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA;
+            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE;
         }
         StrictJarFile jarFile = null;
         try {
@@ -199,17 +199,21 @@
             boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA);
             boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA);
             if (hasProfile && hasVdex) {
-                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX;
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX;
             } else if (hasProfile) {
-                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE;
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE;
             } else if (hasVdex) {
-                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX;
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX;
             } else {
-                return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA;
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN;
             }
         } catch (IOException ignore) {
             Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath);
-            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ERROR_DEX_METADATA;
+            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_ERROR;
         } finally {
             try {
                 if (jarFile != null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 349561d..37f3175 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -986,7 +986,7 @@
      * Fetches the battery manager object and caches it if it hasn't been fetched already.
      */
     private BatteryManager getBatteryManager() {
-        if (mBatteryManager == null) {
+        if (mBatteryManager == null && mContext != null) {
             mBatteryManager = mContext.getSystemService(BatteryManager.class);
         }
 
@@ -1008,10 +1008,6 @@
                     && mPowerManager.getCurrentThermalStatus()
                         >= PowerManager.THERMAL_STATUS_SEVERE);
 
-        if (DEBUG) {
-            Log.d(TAG, "Battery, thermal, or memory are critical: " + isBtmCritical);
-        }
-
         return isBtmCritical;
     }
 
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index b61fd8d..39ed488 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -21,7 +21,6 @@
 import android.annotation.UserIdInt;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
-import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.pm.verify.domain.DomainVerificationState;
 import android.os.UserHandle;
 import android.util.ArrayMap;
@@ -151,7 +150,7 @@
                 Integer state = reusedMap.valueAt(stateIndex);
                 writer.print(domain);
                 writer.print(": ");
-                writer.println(DomainVerificationManager.stateToDebugString(state));
+                writer.println(DomainVerificationState.stateToDebugString(state));
             }
             writer.decreaseIndent();
             writer.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index 1721a18..f4bcd3e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -141,6 +141,12 @@
                     "Caller is not allowed to edit other users");
         }
 
+        if (!mCallback.doesUserExist(callingUserId)) {
+            throw new SecurityException("User " + callingUserId + " does not exist");
+        } else if (!mCallback.doesUserExist(targetUserId)) {
+            throw new SecurityException("User " + targetUserId + " does not exist");
+        }
+
         return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
     }
 
@@ -161,6 +167,12 @@
                 Binder.getCallingPid(), callingUid,
                 "Caller is not allowed to edit user selections");
 
+        if (!mCallback.doesUserExist(callingUserId)) {
+            throw new SecurityException("User " + callingUserId + " does not exist");
+        } else if (!mCallback.doesUserExist(targetUserId)) {
+            throw new SecurityException("User " + targetUserId + " does not exist");
+        }
+
         if (packageName == null) {
             return true;
         }
@@ -184,6 +196,12 @@
             }
         }
 
+        if (!mCallback.doesUserExist(callingUserId)) {
+            throw new SecurityException("User " + callingUserId + " does not exist");
+        } else if (!mCallback.doesUserExist(targetUserId)) {
+            throw new SecurityException("User " + targetUserId + " does not exist");
+        }
+
         return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
     }
 
@@ -197,6 +215,12 @@
                     "Caller is not allowed to edit other users");
         }
 
+        if (!mCallback.doesUserExist(callingUserId)) {
+            throw new SecurityException("User " + callingUserId + " does not exist");
+        } else if (!mCallback.doesUserExist(targetUserId)) {
+            throw new SecurityException("User " + targetUserId + " does not exist");
+        }
+
         return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
     }
 
@@ -221,6 +245,12 @@
         mContext.enforcePermission(
                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
                 callingPid, callingUid, "Caller is not allowed to query user selections");
+
+        if (!mCallback.doesUserExist(callingUserId)) {
+            throw new SecurityException("User " + callingUserId + " does not exist");
+        } else if (!mCallback.doesUserExist(targetUserId)) {
+            throw new SecurityException("User " + targetUserId + " does not exist");
+        }
     }
 
     public interface Callback {
@@ -229,5 +259,7 @@
          * if the package was not installed
          */
         boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId);
+
+        boolean doesUserExist(@UserIdInt int userId);
     }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index b4bcde7..7318273 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -333,10 +333,10 @@
     @Nullable
     UUID getDomainVerificationInfoId(@NonNull String packageName);
 
+    @DomainVerificationManager.Error
     @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
-    void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
-            @NonNull Set<String> domains, int state)
-            throws IllegalArgumentException, NameNotFoundException;
+    int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+            @NonNull Set<String> domains, int state) throws NameNotFoundException;
 
 
     interface Connection extends DomainVerificationEnforcer.Callback {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index a7a52e0..2a17c6d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -24,7 +24,6 @@
 import android.content.pm.verify.domain.DomainSet;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
 import android.content.pm.verify.domain.DomainVerificationUserState;
 import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.os.ServiceSpecificException;
@@ -61,11 +60,12 @@
         }
     }
 
+    @DomainVerificationManager.Error
     @Override
-    public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
+    public int setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
             int state) {
         try {
-            mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
+            return mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
                     domainSet.getDomains(), state);
         } catch (Exception e) {
             throw rethrow(e);
@@ -82,11 +82,12 @@
         }
     }
 
+    @DomainVerificationManager.Error
     @Override
-    public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
+    public int setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
             boolean enabled, @UserIdInt int userId) {
         try {
-            mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
+            return mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
                     domainSet.getDomains(), enabled, userId);
         } catch (Exception e) {
             throw rethrow(e);
@@ -116,14 +117,9 @@
     }
 
     private RuntimeException rethrow(Exception exception) throws RuntimeException {
-        if (exception instanceof InvalidDomainSetException) {
-            int packedErrorCode = DomainVerificationManager.ERROR_INVALID_DOMAIN_SET;
-            packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
-            return new ServiceSpecificException(packedErrorCode,
-                    ((InvalidDomainSetException) exception).getPackageName());
-        } else if (exception instanceof NameNotFoundException) {
+        if (exception instanceof NameNotFoundException) {
             return new ServiceSpecificException(
-                    DomainVerificationManager.ERROR_NAME_NOT_FOUND);
+                    DomainVerificationManager.INTERNAL_ERROR_NAME_NOT_FOUND);
         } else if (exception instanceof RuntimeException) {
             return (RuntimeException) exception;
         } else {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index e85bbe4..a0e252a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -19,6 +19,7 @@
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptySet;
 
+import android.annotation.CheckResult;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -34,7 +35,6 @@
 import android.content.pm.verify.domain.DomainOwner;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager;
-import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
 import android.content.pm.verify.domain.DomainVerificationState;
 import android.content.pm.verify.domain.DomainVerificationUserState;
 import android.content.pm.verify.domain.IDomainVerificationManager;
@@ -243,17 +243,17 @@
             throws NameNotFoundException {
         mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
         synchronized (mLock) {
-            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
-            if (pkgState == null) {
-                return null;
-            }
-
             AndroidPackage pkg = mConnection.getPackageLocked(packageName);
             if (pkg == null) {
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
-            Map<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                return null;
+            }
+
+            ArrayMap<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
 
             // TODO(b/159952358): Should the domain list be cached?
             ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
@@ -267,44 +267,56 @@
                         DomainVerificationState.STATE_NO_RESPONSE);
             }
 
+            final int mapSize = hostToStateMap.size();
+            for (int index = 0; index < mapSize; index++) {
+                int internalValue = hostToStateMap.valueAt(index);
+                int publicValue = DomainVerificationState.convertToInfoState(internalValue);
+                hostToStateMap.setValueAt(index, publicValue);
+            }
+
             // TODO(b/159952358): Do not return if no values are editable (all ignored states)?
             return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
         }
     }
 
-    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
-            int state) throws InvalidDomainSetException, NameNotFoundException {
+    @DomainVerificationManager.Error
+    public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+            int state) throws NameNotFoundException {
         if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
             if (state != DomainVerificationState.STATE_SUCCESS) {
-                throw new IllegalArgumentException(
-                        "Verifier can only set STATE_SUCCESS or codes greater than or equal to "
-                                + "STATE_FIRST_VERIFIER_DEFINED");
+                return DomainVerificationManager.ERROR_INVALID_STATE_CODE;
             }
         }
 
-        setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId, domains,
-                state);
+        return setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId,
+                domains, state);
     }
 
+    @DomainVerificationManager.Error
     @Override
-    public void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+    public int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
             @NonNull Set<String> domains, int state)
-            throws InvalidDomainSetException, NameNotFoundException {
+            throws NameNotFoundException {
         mEnforcer.assertApprovedVerifier(callingUid, mProxy);
         synchronized (mLock) {
             List<String> verifiedDomains = new ArrayList<>();
 
-            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+            GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
                     true /* forAutoVerify */, callingUid, null /* userId */);
+            if (result.isError()) {
+                return result.getErrorCode();
+            }
+
+            DomainVerificationPkgState pkgState = result.getPkgState();
             ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
             for (String domain : domains) {
                 Integer previousState = stateMap.get(domain);
                 if (previousState != null
-                        && !DomainVerificationManager.isStateModifiable(previousState)) {
+                        && !DomainVerificationState.isModifiable(previousState)) {
                     continue;
                 }
 
-                if (DomainVerificationManager.isStateVerified(state)) {
+                if (DomainVerificationState.isVerified(state)) {
                     verifiedDomains.add(domain);
                 }
 
@@ -318,6 +330,7 @@
         }
 
         mConnection.scheduleWriteSettings();
+        return DomainVerificationManager.STATUS_OK;
     }
 
     @Override
@@ -460,17 +473,24 @@
                     throw DomainVerificationUtils.throwPackageUnavailable(packageName);
                 }
 
-                pkgState.getOrCreateUserState(userId)
-                        .setLinkHandlingAllowed(allowed);
+                if (userId == UserHandle.USER_ALL) {
+                    for (int aUserId : mConnection.getAllUserIds()) {
+                        pkgState.getOrCreateUserState(aUserId)
+                                .setLinkHandlingAllowed(allowed);
+                    }
+                } else {
+                    pkgState.getOrCreateUserState(userId)
+                            .setLinkHandlingAllowed(allowed);
+                }
             }
         }
 
         mConnection.scheduleWriteSettings();
     }
 
-    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+    public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
             @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
-            throws InvalidDomainSetException, NameNotFoundException {
+            throws NameNotFoundException {
         synchronized (mLock) {
             final int callingUid = mConnection.getCallingUid();
             // Pass null for package name here and do the app visibility enforcement inside
@@ -478,14 +498,17 @@
             // ID reason if the target app is invisible
             if (!mEnforcer.assertApprovedUserSelector(callingUid, mConnection.getCallingUserId(),
                     null /* packageName */, userId)) {
-                throw new InvalidDomainSetException(domainSetId, null,
-                        InvalidDomainSetException.REASON_ID_INVALID);
+                return DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID;
             }
 
-            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+            GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
                     false /* forAutoVerify */, callingUid, userId);
-            DomainVerificationInternalUserState userState =
-                    pkgState.getOrCreateUserState(userId);
+            if (result.isError()) {
+                return result.getErrorCode();
+            }
+
+            DomainVerificationPkgState pkgState = result.getPkgState();
+            DomainVerificationInternalUserState userState = pkgState.getOrCreateUserState(userId);
 
             // Disable other packages if approving this one. Note that this check is only done for
             // enabling. This allows an escape hatch in case multiple packages somehow get selected.
@@ -503,8 +526,7 @@
                             userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked);
                     int highestApproval = packagesToLevel.second;
                     if (highestApproval > APPROVAL_LEVEL_SELECTION) {
-                        throw new InvalidDomainSetException(domainSetId, null,
-                                InvalidDomainSetException.REASON_UNABLE_TO_APPROVE);
+                        return DomainVerificationManager.ERROR_UNABLE_TO_APPROVE;
                     }
 
                     domainToApprovedPackages.put(domain, packagesToLevel.first);
@@ -544,6 +566,7 @@
         }
 
         mConnection.scheduleWriteSettings();
+        return DomainVerificationManager.STATUS_OK;
     }
 
     @Override
@@ -636,16 +659,16 @@
             throw DomainVerificationUtils.throwPackageUnavailable(packageName);
         }
         synchronized (mLock) {
-            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
-            if (pkgState == null) {
-                return null;
-            }
-
             AndroidPackage pkg = mConnection.getPackageLocked(packageName);
             if (pkg == null) {
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                return null;
+            }
+
             ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg);
             int webDomainsSize = webDomains.size();
 
@@ -659,7 +682,7 @@
                 Integer state = stateMap.get(host);
 
                 int domainState;
-                if (state != null && DomainVerificationManager.isStateVerified(state)) {
+                if (state != null && DomainVerificationState.isVerified(state)) {
                     domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
                 } else if (enabledHosts.contains(host)) {
                     domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
@@ -793,19 +816,12 @@
                 Integer oldStateInteger = oldStateMap.get(domain);
                 if (oldStateInteger != null) {
                     int oldState = oldStateInteger;
-                    switch (oldState) {
-                        case DomainVerificationState.STATE_SUCCESS:
-                        case DomainVerificationState.STATE_RESTORED:
-                        case DomainVerificationState.STATE_MIGRATED:
-                            newStateMap.put(domain, oldState);
-                            break;
-                        default:
-                            // In all other cases, the state code is left unset
-                            // (STATE_NO_RESPONSE) to signal to the verification agent that any
-                            // existing error has been cleared and the domain should be
-                            // re-attempted. This makes update of a package a signal to
-                            // re-verify.
-                            break;
+                    // If the following case fails, the state code is left unset
+                    // (STATE_NO_RESPONSE) to signal to the verification agent that any existing
+                    // error has been cleared and the domain should be re-attempted. This makes
+                    // update of a package a signal to re-verify.
+                    if (DomainVerificationState.shouldMigrate(oldState)) {
+                        newStateMap.put(domain, oldState);
                     }
                 }
             }
@@ -858,13 +874,13 @@
         boolean sendBroadcast = true;
 
         DomainVerificationPkgState pkgState;
-        pkgState = mSettings.getPendingState(pkgName);
+        pkgState = mSettings.removePendingState(pkgName);
         if (pkgState != null) {
             // Don't send when attaching from pending read, which is usually boot scan. Re-send on
             // boot is handled in a separate method once all packages are added.
             sendBroadcast = false;
         } else {
-            pkgState = mSettings.getRestoredState(pkgName);
+            pkgState = mSettings.removeRestoredState(pkgName);
         }
 
         AndroidPackage pkg = newPkgSetting.getPkg();
@@ -872,7 +888,7 @@
         boolean hasAutoVerifyDomains = !domains.isEmpty();
         boolean isPendingOrRestored = pkgState != null;
         if (isPendingOrRestored) {
-            pkgState.setId(domainSetId);
+            pkgState = new DomainVerificationPkgState(pkgState, domainSetId, hasAutoVerifyDomains);
         } else {
             pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
         }
@@ -1097,28 +1113,25 @@
      * @param userIdForFilter which user to filter app access to, or null if the caller has already
      *                        validated package visibility
      */
+    @CheckResult
     @GuardedBy("mLock")
-    private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
+    private GetAttachedResult getAndValidateAttachedLocked(@NonNull UUID domainSetId,
             @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
-            @Nullable Integer userIdForFilter)
-            throws InvalidDomainSetException, NameNotFoundException {
+            @Nullable Integer userIdForFilter) throws NameNotFoundException {
         if (domainSetId == null) {
-            throw new InvalidDomainSetException(null, null,
-                    InvalidDomainSetException.REASON_ID_NULL);
+            return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL);
         }
 
         DomainVerificationPkgState pkgState = mAttachedPkgStates.get(domainSetId);
         if (pkgState == null) {
-            throw new InvalidDomainSetException(domainSetId, null,
-                    InvalidDomainSetException.REASON_ID_INVALID);
+            return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
         }
 
         String pkgName = pkgState.getPackageName();
 
         if (userIdForFilter != null
                 && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
-            throw new InvalidDomainSetException(domainSetId, null,
-                    InvalidDomainSetException.REASON_ID_INVALID);
+            return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
         }
 
         PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
@@ -1127,8 +1140,8 @@
         }
 
         if (CollectionUtils.isEmpty(domains)) {
-            throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
-                    InvalidDomainSetException.REASON_SET_NULL_OR_EMPTY);
+            return GetAttachedResult.error(
+                    DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY);
         }
         AndroidPackage pkg = pkgSetting.getPkg();
         ArraySet<String> declaredDomains = forAutoVerify
@@ -1136,11 +1149,10 @@
                 : mCollector.collectAllWebDomains(pkg);
 
         if (domains.retainAll(declaredDomains)) {
-            throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
-                    InvalidDomainSetException.REASON_UNKNOWN_DOMAIN);
+            return GetAttachedResult.error(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN);
         }
 
-        return pkgState;
+        return GetAttachedResult.success(pkgState);
     }
 
     @Override
@@ -1185,7 +1197,7 @@
     /**
      * Determine whether or not a broadcast should be sent at boot for the given {@param pkgState}.
      * Sends only if the only states recorded are default as decided by {@link
-     * DomainVerificationManager#isStateDefault(int)}.
+     * DomainVerificationState#isDefault(int)}.
      *
      * If any other state is set, it's assumed that the domain verification agent is aware of the
      * package and has already scheduled future verification requests.
@@ -1199,7 +1211,7 @@
         int statesSize = stateMap.size();
         for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
             Integer state = stateMap.valueAt(stateIndex);
-            if (!DomainVerificationManager.isStateDefault(state)) {
+            if (!DomainVerificationState.isDefault(state)) {
                 return false;
             }
         }
@@ -1318,83 +1330,50 @@
             @NonNull Function<String, PackageSetting> pkgSettingFunction) {
         String domain = intent.getData().getHost();
 
-        // Collect package names
-        ArrayMap<String, Integer> packageApprovals = new ArrayMap<>();
+        // Collect valid infos
+        ArrayMap<ResolveInfo, Integer> infoApprovals = new ArrayMap<>();
         int infosSize = infos.size();
         for (int index = 0; index < infosSize; index++) {
-            packageApprovals.put(infos.get(index).getComponentInfo().packageName,
-                    APPROVAL_LEVEL_NONE);
+            final ResolveInfo info = infos.get(index);
+            // Only collect for intent filters that can auto resolve
+            if (info.isAutoResolutionAllowed()) {
+                infoApprovals.put(info, null);
+            }
         }
 
         // Find all approval levels
-        int highestApproval = fillMapWithApprovalLevels(packageApprovals, domain, userId,
+        int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId,
                 pkgSettingFunction);
         if (highestApproval == APPROVAL_LEVEL_NONE) {
             return Pair.create(emptyList(), highestApproval);
         }
 
-        // Filter to highest, non-zero packages
-        ArraySet<String> approvedPackages = new ArraySet<>();
-        int approvalsSize = packageApprovals.size();
-        for (int index = 0; index < approvalsSize; index++) {
-            if (packageApprovals.valueAt(index) == highestApproval) {
-                approvedPackages.add(packageApprovals.keyAt(index));
+        // Filter to highest, non-zero infos
+        for (int index = infoApprovals.size() - 1; index >= 0; index--) {
+            if (infoApprovals.valueAt(index) != highestApproval) {
+                infoApprovals.removeAt(index);
             }
         }
 
-        ArraySet<String> filteredPackages = new ArraySet<>();
-        if (highestApproval == APPROVAL_LEVEL_LEGACY_ASK) {
+        if (highestApproval != APPROVAL_LEVEL_LEGACY_ASK) {
             // To maintain legacy behavior while the Settings API is not implemented,
             // show the chooser if all approved apps are marked ask, skipping the
             // last app, last declaration filtering.
-            filteredPackages.addAll(approvedPackages);
-        } else {
-            // Filter to last installed package
-            long latestInstall = Long.MIN_VALUE;
-            int approvedSize = approvedPackages.size();
-            for (int index = 0; index < approvedSize; index++) {
-                String packageName = approvedPackages.valueAt(index);
-                PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
-                if (pkgSetting == null) {
-                    continue;
-                }
-                long installTime = pkgSetting.getFirstInstallTime();
-                if (installTime > latestInstall) {
-                    latestInstall = installTime;
-                    filteredPackages.clear();
-                    filteredPackages.add(packageName);
-                } else if (installTime == latestInstall) {
-                    filteredPackages.add(packageName);
-                }
-            }
+            filterToLastFirstInstalled(infoApprovals, pkgSettingFunction);
         }
 
-        // Filter to approved ResolveInfos
-        ArrayMap<String, List<ResolveInfo>> approvedInfos = new ArrayMap<>();
-        for (int index = 0; index < infosSize; index++) {
-            ResolveInfo info = infos.get(index);
-            String packageName = info.getComponentInfo().packageName;
-            if (filteredPackages.contains(packageName)) {
-                List<ResolveInfo> infosPerPackage = approvedInfos.get(packageName);
-                if (infosPerPackage == null) {
-                    infosPerPackage = new ArrayList<>();
-                    approvedInfos.put(packageName, infosPerPackage);
-                }
-                infosPerPackage.add(info);
-            }
+        // Easier to transform into list as the filterToLastDeclared method
+        // requires swapping indexes, which doesn't work with ArrayMap keys
+        final int size = infoApprovals.size();
+        List<ResolveInfo> finalList = new ArrayList<>(size);
+        for (int index = 0; index < size; index++) {
+            finalList.add(infoApprovals.keyAt(index));
         }
 
-        List<ResolveInfo> finalList;
-        if (highestApproval == APPROVAL_LEVEL_LEGACY_ASK) {
-            // If legacy ask, skip the last declaration filtering
-            finalList = new ArrayList<>();
-            int size = approvedInfos.size();
-            for (int index = 0; index < size; index++) {
-                finalList.addAll(approvedInfos.valueAt(index));
-            }
-        } else {
+        // If legacy ask, skip the last declaration filtering
+        if (highestApproval != APPROVAL_LEVEL_LEGACY_ASK) {
             // Find the last declared ResolveInfo per package
-            finalList = filterToLastDeclared(approvedInfos, pkgSettingFunction);
+            filterToLastDeclared(finalList, pkgSettingFunction);
         }
 
         return Pair.create(finalList, highestApproval);
@@ -1403,68 +1382,127 @@
     /**
      * @return highest approval level found
      */
-    private int fillMapWithApprovalLevels(@NonNull ArrayMap<String, Integer> inputMap,
+    @ApprovalLevel
+    private int fillMapWithApprovalLevels(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
             @NonNull String domain, @UserIdInt int userId,
             @NonNull Function<String, PackageSetting> pkgSettingFunction) {
         int highestApproval = APPROVAL_LEVEL_NONE;
         int size = inputMap.size();
         for (int index = 0; index < size; index++) {
-            String packageName = inputMap.keyAt(index);
+            if (inputMap.valueAt(index) != null) {
+                // Already filled by previous iteration
+                continue;
+            }
+
+            ResolveInfo info = inputMap.keyAt(index);
+            final String packageName = info.getComponentInfo().packageName;
             PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
             if (pkgSetting == null) {
-                inputMap.setValueAt(index, APPROVAL_LEVEL_NONE);
+                fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE);
                 continue;
             }
             int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain);
             highestApproval = Math.max(highestApproval, approval);
-            inputMap.setValueAt(index, approval);
+            fillInfoMapForSamePackage(inputMap, packageName, approval);
         }
 
         return highestApproval;
     }
 
-    @NonNull
-    private List<ResolveInfo> filterToLastDeclared(
-            @NonNull ArrayMap<String, List<ResolveInfo>> inputMap,
-            @NonNull Function<String, PackageSetting> pkgSettingFunction) {
-        List<ResolveInfo> finalList = new ArrayList<>(inputMap.size());
+    private void fillInfoMapForSamePackage(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
+            @NonNull String targetPackageName, @ApprovalLevel int level) {
+        final int size = inputMap.size();
+        for (int index = 0; index < size; index++) {
+            final String packageName = inputMap.keyAt(index).getComponentInfo().packageName;
+            if (Objects.equals(targetPackageName, packageName)) {
+                inputMap.setValueAt(index, level);
+            }
+        }
+    }
 
-        int inputSize = inputMap.size();
-        for (int inputIndex = 0; inputIndex < inputSize; inputIndex++) {
-            String packageName = inputMap.keyAt(inputIndex);
-            List<ResolveInfo> infos = inputMap.valueAt(inputIndex);
+    @NonNull
+    private void filterToLastFirstInstalled(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
+            @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+        // First, find the package with the latest first install time
+        String targetPackageName = null;
+        long latestInstall = Long.MIN_VALUE;
+        final int size = inputMap.size();
+        for (int index = 0; index < size; index++) {
+            ResolveInfo info = inputMap.keyAt(index);
+            String packageName = info.getComponentInfo().packageName;
             PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+            if (pkgSetting == null) {
+                continue;
+            }
+
+            long installTime = pkgSetting.getFirstInstallTime();
+            if (installTime > latestInstall) {
+                latestInstall = installTime;
+                targetPackageName = packageName;
+            }
+        }
+
+        // Then, remove all infos that don't match the package
+        for (int index = inputMap.size() - 1; index >= 0; index--) {
+            ResolveInfo info = inputMap.keyAt(index);
+            if (!Objects.equals(targetPackageName, info.getComponentInfo().packageName)) {
+                inputMap.removeAt(index);
+            }
+        }
+    }
+
+    @NonNull
+    private void filterToLastDeclared(@NonNull List<ResolveInfo> inputList,
+            @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+        // Must call size each time as the size of the list will decrease
+        for (int index = 0; index < inputList.size(); index++) {
+            ResolveInfo info = inputList.get(index);
+            String targetPackageName = info.getComponentInfo().packageName;
+            PackageSetting pkgSetting = pkgSettingFunction.apply(targetPackageName);
             AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
             if (pkg == null) {
                 continue;
             }
 
-            ResolveInfo result = null;
-            int highestIndex = -1;
-            int infosSize = infos.size();
-            for (int infoIndex = 0; infoIndex < infosSize; infoIndex++) {
-                ResolveInfo info = infos.get(infoIndex);
-                List<ParsedActivity> activities = pkg.getActivities();
-                int activitiesSize = activities.size();
-                for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
-                    if (Objects.equals(activities.get(activityIndex).getComponentName(),
-                            info.getComponentInfo().getComponentName())) {
-                        if (activityIndex > highestIndex) {
-                            highestIndex = activityIndex;
-                            result = info;
-                        }
-                        break;
-                    }
+            ResolveInfo result = info;
+            int highestIndex = indexOfIntentFilterEntry(pkg, result);
+
+            // Search backwards so that lower results can be removed as they're found
+            for (int searchIndex = inputList.size() - 1; searchIndex >= index + 1; searchIndex--) {
+                ResolveInfo searchInfo = inputList.get(searchIndex);
+                if (!Objects.equals(targetPackageName, searchInfo.getComponentInfo().packageName)) {
+                    continue;
                 }
+
+                int entryIndex = indexOfIntentFilterEntry(pkg, searchInfo);
+                if (entryIndex > highestIndex) {
+                    highestIndex = entryIndex;
+                    result = searchInfo;
+                }
+
+                // Always remove the entry so that the current index
+                // is left as the sole candidate of the target package
+                inputList.remove(searchIndex);
             }
 
-            // Shouldn't be null, but might as well be safe
-            if (result != null) {
-                finalList.add(result);
+            // Swap the current index for the result, leaving this as
+            // the only entry with the target package name
+            inputList.set(index, result);
+        }
+    }
+
+    private int indexOfIntentFilterEntry(@NonNull AndroidPackage pkg,
+            @NonNull ResolveInfo target) {
+        List<ParsedActivity> activities = pkg.getActivities();
+        int activitiesSize = activities.size();
+        for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+            if (Objects.equals(activities.get(activityIndex).getComponentName(),
+                    target.getComponentInfo().getComponentName())) {
+                return activityIndex;
             }
         }
 
-        return finalList;
+        return -1;
     }
 
     @Override
@@ -1472,8 +1510,7 @@
             @NonNull List<ResolveInfo> candidates,
             @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
         String packageName = pkgSetting.getName();
-        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
-                resolveInfoFlags)) {
+        if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
             if (DEBUG_APPROVAL) {
                 debugApproval(packageName, intent, userId, false, "not valid intent");
             }
@@ -1542,7 +1579,7 @@
             ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
             // Check if the exact host matches
             Integer state = stateMap.get(host);
-            if (state != null && DomainVerificationManager.isStateVerified(state)) {
+            if (state != null && DomainVerificationState.isVerified(state)) {
                 if (DEBUG_APPROVAL) {
                     debugApproval(packageName, debugObject, userId, true,
                             "host verified exactly");
@@ -1553,7 +1590,7 @@
             // Otherwise see if the host matches a verified domain by wildcard
             int stateMapSize = stateMap.size();
             for (int index = 0; index < stateMapSize; index++) {
-                if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) {
+                if (!DomainVerificationState.isVerified(stateMap.valueAt(index))) {
                     continue;
                 }
 
@@ -1672,4 +1709,40 @@
         Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for "
                 + debugObject + " for user " + userId + ": " + reason);
     }
+
+    private static class GetAttachedResult {
+
+        @Nullable
+        private DomainVerificationPkgState mPkgState;
+
+        private int mErrorCode;
+
+        GetAttachedResult(@Nullable DomainVerificationPkgState pkgState, int errorCode) {
+            mPkgState = pkgState;
+            mErrorCode = errorCode;
+        }
+
+        @NonNull
+        static GetAttachedResult error(@DomainVerificationManager.Error int errorCode) {
+            return new GetAttachedResult(null, errorCode);
+        }
+
+        @NonNull
+        static GetAttachedResult success(@NonNull DomainVerificationPkgState pkgState) {
+            return new GetAttachedResult(pkgState, DomainVerificationManager.STATUS_OK);
+        }
+
+        @NonNull
+        DomainVerificationPkgState getPkgState() {
+            return mPkgState;
+        }
+
+        boolean isError() {
+            return mErrorCode != DomainVerificationManager.STATUS_OK;
+        }
+
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index f3d1dbb..8b59da7 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -256,16 +256,16 @@
     }
 
     @Nullable
-    public DomainVerificationPkgState getPendingState(@NonNull String pkgName) {
+    public DomainVerificationPkgState removePendingState(@NonNull String pkgName) {
         synchronized (mLock) {
-            return mPendingPkgStates.get(pkgName);
+            return mPendingPkgStates.remove(pkgName);
         }
     }
 
     @Nullable
-    public DomainVerificationPkgState getRestoredState(@NonNull String pkgName) {
+    public DomainVerificationPkgState removeRestoredState(@NonNull String pkgName) {
         synchronized (mLock) {
-            return mRestoredPkgStates.get(pkgName);
+            return mRestoredPkgStates.remove(pkgName);
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index 94767f5..7e755fa 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -38,6 +38,8 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
 
 public class DomainVerificationShell {
 
@@ -226,22 +228,19 @@
 
         userId = translateUserId(userId, "runSetAppLinksUserState");
 
-        String enabledString = commandHandler.getNextArgRequired();
+        String enabledArg = commandHandler.getNextArg();
+        if (TextUtils.isEmpty(enabledArg)) {
+            commandHandler.getErrPrintWriter().println("Error: enabled param not specified");
+            return false;
+        }
 
-        // Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
-        // accidentally parsed as a boolean
         boolean enabled;
-        switch (enabledString) {
-            case "true":
-                enabled = true;
-                break;
-            case "false":
-                enabled = false;
-                break;
-            default:
-                commandHandler.getErrPrintWriter().println(
-                        "Invalid enabled param: " + enabledString);
-                return false;
+        try {
+            enabled = parseEnabled(enabledArg);
+        } catch (IllegalArgumentException e) {
+            commandHandler.getErrPrintWriter()
+                    .println("Error: invalid enabled param: " + e.getMessage());
+            return false;
         }
 
         ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
@@ -255,8 +254,8 @@
         }
 
         try {
-            mCallback.setDomainVerificationUserSelectionInternal(userId,
-                    packageName, enabled, domains);
+            mCallback.setDomainVerificationUserSelectionInternal(userId, packageName, enabled,
+                    domains);
         } catch (NameNotFoundException e) {
             commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
             return false;
@@ -362,15 +361,12 @@
     private boolean runSetAppLinksAllowed(@NonNull BasicShellCommandHandler commandHandler) {
         String packageName = null;
         Integer userId = null;
-        Boolean allowed = null;
         String option;
         while ((option = commandHandler.getNextOption()) != null) {
             if (option.equals("--package")) {
-                packageName = commandHandler.getNextArgRequired();
-            } if (option.equals("--user")) {
+                packageName = commandHandler.getNextArg();
+            } else if (option.equals("--user")) {
                 userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
-            } else if (allowed == null) {
-                allowed = Boolean.valueOf(option);
             } else {
                 commandHandler.getErrPrintWriter().println("Error: unexpected option: " + option);
                 return false;
@@ -389,11 +385,21 @@
             return false;
         }
 
-        if (allowed == null) {
+        String allowedArg = commandHandler.getNextArg();
+        if (TextUtils.isEmpty(allowedArg)) {
             commandHandler.getErrPrintWriter().println("Error: allowed setting not specified");
             return false;
         }
 
+        boolean allowed;
+        try {
+            allowed = parseEnabled(allowedArg);
+        } catch (IllegalArgumentException e) {
+            commandHandler.getErrPrintWriter()
+                    .println("Error: invalid allowed setting: " + e.getMessage());
+            return false;
+        }
+
         userId = translateUserId(userId, "runSetAppLinksAllowed");
 
         try {
@@ -422,6 +428,22 @@
     }
 
     /**
+     * Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
+     * accidentally parsed as a boolean.
+     */
+    @NonNull
+    private boolean parseEnabled(@NonNull String arg) throws IllegalArgumentException {
+        switch (arg.toLowerCase(Locale.US)) {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            default:
+                throw new IllegalArgumentException(arg + " is not a valid boolean");
+        }
+    }
+
+    /**
      * Separated interface from {@link DomainVerificationManagerInternal} to hide methods that are
      * even more internal, and so that testing is easier.
      */
@@ -498,7 +520,8 @@
         void verifyPackages(@Nullable List<String> packageNames, boolean reVerify);
 
         /**
-         * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer)
+         * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer,
+         * Function)
          */
         void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
                 @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 883bbad..cb3b5c9 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -22,7 +22,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
 import android.os.Binder;
 
 import com.android.internal.util.CollectionUtils;
@@ -30,7 +29,6 @@
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
-import java.util.List;
 import java.util.Set;
 
 public final class DomainVerificationUtils {
@@ -46,7 +44,6 @@
     }
 
     public static boolean isDomainVerificationIntent(Intent intent,
-            @NonNull List<ResolveInfo> candidates,
             @PackageManager.ResolveInfoFlags int resolveInfoFlags) {
         if (!intent.isWebIntent()) {
             return false;
@@ -63,42 +60,18 @@
                     && intent.hasCategory(Intent.CATEGORY_BROWSABLE);
         }
 
-            // In cases where at least one browser is resolved and only one non-browser is resolved,
-        // the Intent is coerced into an app links intent, under the assumption the browser can
-        // be skipped if the app is approved at any level for the domain.
-        boolean foundBrowser = false;
-        boolean foundOneApp = false;
-
-        final int candidatesSize = candidates.size();
-        for (int index = 0; index < candidatesSize; index++) {
-            final ResolveInfo info = candidates.get(index);
-            if (info.handleAllWebDataURI) {
-                foundBrowser = true;
-            } else if (foundOneApp) {
-                // Already true, so duplicate app
-                foundOneApp = false;
-                break;
-            } else {
-                foundOneApp = true;
-            }
-        }
-
         boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
-        boolean onlyOneNonBrowser = foundBrowser && foundOneApp;
 
         // Check if matches (BROWSABLE || none) && DEFAULT
         if (categoriesSize == 0) {
-            // No categories, run coerce case, matching DEFAULT by flags
-            return onlyOneNonBrowser && matchDefaultByFlags;
-        } else if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) {
-            // Run coerce case, matching by explicit DEFAULT
-            return onlyOneNonBrowser;
+            // No categories, only allow matching DEFAULT by flags
+            return matchDefaultByFlags;
         } else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
             // Intent matches BROWSABLE, must match DEFAULT by flags
             return matchDefaultByFlags;
         } else {
-            // Otherwise not matching any app link categories
-            return false;
+            // Otherwise only needs to have DEFAULT
+            return intent.hasCategory(Intent.CATEGORY_DEFAULT);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index a089a60..40c7091 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -68,6 +68,12 @@
         this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
     }
 
+    public DomainVerificationPkgState(@NonNull DomainVerificationPkgState pkgState,
+            @NonNull UUID id, boolean hasAutoVerifyDomains) {
+        this(pkgState.getPackageName(), id, hasAutoVerifyDomains, pkgState.getStateMap(),
+                pkgState.getUserStates());
+    }
+
     @Nullable
     public DomainVerificationInternalUserState getUserState(@UserIdInt int userId) {
         return mUserStates.get(userId);
@@ -84,10 +90,6 @@
         return userState;
     }
 
-    public void setId(@NonNull UUID id) {
-        mId = id;
-    }
-
     public void removeUser(@UserIdInt int userId) {
         mUserStates.remove(userId);
     }
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index 18042af..fa36683 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -207,20 +207,24 @@
                 int callingUid = response.callingUid;
                 if (!successfulDomains.isEmpty()) {
                     try {
-                        mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
-                                successfulDomains, DomainVerificationState.STATE_SUCCESS);
-                    } catch (DomainVerificationManager.InvalidDomainSetException
-                            | PackageManager.NameNotFoundException e) {
+                        if (mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+                                successfulDomains, DomainVerificationState.STATE_SUCCESS)
+                                != DomainVerificationManager.STATUS_OK) {
+                            Slog.e(TAG, "Failure reporting successful domains for " + packageName);
+                        }
+                    } catch (Exception e) {
                         Slog.e(TAG, "Failure reporting successful domains for " + packageName, e);
                     }
                 }
 
                 if (!failedDomains.isEmpty()) {
                     try {
-                        mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
-                                failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE);
-                    } catch (DomainVerificationManager.InvalidDomainSetException
-                            | PackageManager.NameNotFoundException e) {
+                        if (mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+                                failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE)
+                                != DomainVerificationManager.STATUS_OK) {
+                            Slog.e(TAG, "Failure reporting failed domains for " + packageName);
+                        }
+                    } catch (Exception e) {
                         Slog.e(TAG, "Failure reporting failed domains for " + packageName, e);
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a909c6d..d361a8c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1479,7 +1479,7 @@
     private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
         final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
         if (windowSurface != null && windowSurface.isValid()) {
-            Transaction transaction = getPendingTransaction();
+            Transaction transaction = getSyncTransaction();
             transaction.setCornerRadius(windowSurface, cornersRadius);
         }
     }
@@ -1491,7 +1491,7 @@
         }
         layoutLetterbox(winHint);
         if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
-            mLetterbox.applySurfaceChanges(getPendingTransaction());
+            mLetterbox.applySurfaceChanges(getSyncTransaction());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c6cece3..59d40cf 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2707,7 +2707,8 @@
                     launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
                     break;
                 case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
-                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
+                    launchFlags &=
+                            ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
                     break;
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 5ccf576..9855ea5 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -451,7 +451,7 @@
 
     void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
         if (organizer == null) return;
-        migrateToNewSurfaceControl();
+        migrateToNewSurfaceControl(getSyncTransaction());
         mOrganizerController.onDisplayAreaVanished(organizer, this);
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c78f9ec..95b2b5d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3428,7 +3428,7 @@
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
-        assignChildLayers(getPendingTransaction());
+        assignChildLayers(getSyncTransaction());
         if (setLayoutNeeded) {
             setLayoutNeeded();
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ad0ce5b..09a8e4f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2382,11 +2382,11 @@
     }
 
     @Override
-    void migrateToNewSurfaceControl() {
-        super.migrateToNewSurfaceControl();
+    void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
+        super.migrateToNewSurfaceControl(t);
         mLastSurfaceSize.x = 0;
         mLastSurfaceSize.y = 0;
-        updateSurfaceSize(getPendingTransaction());
+        updateSurfaceSize(t);
     }
 
     void updateSurfaceSize(SurfaceControl.Transaction transaction) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 498fc5c..88e9ae9 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -963,6 +963,22 @@
         }
     }
 
+    @Override
+    void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
+        super.migrateToNewSurfaceControl(t);
+        if (mAppAnimationLayer == null) {
+            return;
+        }
+
+        // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
+        t.reparent(mAppAnimationLayer, mSurfaceControl);
+        t.reparent(mBoostedAppAnimationLayer, mSurfaceControl);
+        t.reparent(mHomeAppAnimationLayer, mSurfaceControl);
+        t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
+        reassignLayer(t);
+        scheduleAnimation();
+    }
+
     void onRootTaskRemoved(Task rootTask) {
         if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
             Slog.v(TAG_ROOT_TASK, "onRootTaskRemoved: detaching " + rootTask + " from displayId="
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 5d22f8f..375b3f4 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -310,7 +310,7 @@
             boolean taskAppearedSent = t.mTaskAppearedSent;
             if (taskAppearedSent) {
                 if (t.getSurfaceControl() != null) {
-                    t.migrateToNewSurfaceControl();
+                    t.migrateToNewSurfaceControl(t.getSyncTransaction());
                 }
                 t.mTaskAppearedSent = false;
             }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8d85958..e3679c0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -389,12 +389,12 @@
             mParent.onChildAdded(this);
         }
         if (!mReparenting) {
+            onSyncReparent(oldParent, mParent);
             if (mParent != null && mParent.mDisplayContent != null
                     && mDisplayContent != mParent.mDisplayContent) {
                 onDisplayChanged(mParent.mDisplayContent);
             }
             onParentChanged(mParent, oldParent);
-            onSyncReparent(oldParent, mParent);
         }
     }
 
@@ -460,8 +460,7 @@
      * This is used to revoke control of the SurfaceControl from a client process that was
      * previously organizing this WindowContainer.
      */
-    void migrateToNewSurfaceControl() {
-        SurfaceControl.Transaction t = getPendingTransaction();
+    void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
         t.remove(mSurfaceControl);
         // Clear the last position so the new SurfaceControl will get correct position
         mLastSurfacePosition.set(0, 0);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 53ebfb2..8148f15 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -451,24 +451,15 @@
     public abstract int getInputMethodWindowVisibleHeight(int displayId);
 
     /**
-     * Notifies WindowManagerService that the current IME window status is being changed.
+     * Notifies WindowManagerService that the expected back-button behavior might have changed.
      *
      * <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
      * tested caller of this method.</p>
      *
-     * @param imeToken token to track the active input method. Corresponding IME windows can be
-     *                 identified by checking {@link android.view.WindowManager.LayoutParams#token}.
-     *                 Note that there is no guarantee that the corresponding window is already
-     *                 created
-     * @param imeWindowVisible whether the active IME thinks that its window should be visible or
-     *                         hidden, no matter how WindowManagerService will react / has reacted
-     *                         to corresponding API calls.  Note that this state is not guaranteed
-     *                         to be synchronized with state in WindowManagerService.
      * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
      *                                   key is expected to dismiss the software keyboard.
      */
-    public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-            boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed);
+    public abstract void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed);
 
     /**
      * Notifies WindowManagerService that the current IME window status is being changed.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d2c9e29..dce79a0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7718,8 +7718,7 @@
         }
 
         @Override
-        public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-                boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
+        public void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed) {
             mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
         }
 
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index a73f6c6..6cb4a63 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -123,16 +123,9 @@
 }
 
 static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) {
-    raise(BIONIC_SIGNAL_FDTRACK);
-
-    // Wait for a bit to allow fdtrack to dump backtraces to logcat.
-    std::this_thread::sleep_for(5s);
-
-    // Abort on a different thread to avoid ART dumping runtime stacks.
-    std::thread([]() {
-        LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd "
-                         "backtraces");
-    }).join();
+    sigval val;
+    val.sival_int = 1;
+    sigqueue(getpid(), BIONIC_SIGNAL_FDTRACK, val);
 }
 
 static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e3fbedd..db70d44 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -96,6 +96,10 @@
     return c;
 }
 
+static bool isPageAligned(IncFsSize s) {
+    return (s & (Constants::blockSize - 1)) == 0;
+}
+
 template <base::LogSeverity level = base::ERROR>
 bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
     auto cstr = path::c_str(name);
@@ -1001,25 +1005,53 @@
 
 int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
                                  incfs::NewFileParams params, std::span<const uint8_t> data) {
-    if (auto ifs = getIfs(storage)) {
-        std::string normPath = normalizePathToStorage(*ifs, storage, path);
-        if (normPath.empty()) {
-            LOG(ERROR) << "Internal error: storageId " << storage
-                       << " failed to normalize: " << path;
+    const auto ifs = getIfs(storage);
+    if (!ifs) {
+        return -EINVAL;
+    }
+    if (data.size() > params.size) {
+        LOG(ERROR) << "Bad data size - bigger than file size";
+        return -EINVAL;
+    }
+    if (!data.empty() && data.size() != params.size) {
+        // Writing a page is an irreversible operation, and it can't be updated with additional
+        // data later. Check that the last written page is complete, or we may break the file.
+        if (!isPageAligned(data.size())) {
+            LOG(ERROR) << "Bad data size - tried to write half a page?";
             return -EINVAL;
         }
-        if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
-            LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
-            return err;
+    }
+    const std::string normPath = normalizePathToStorage(*ifs, storage, path);
+    if (normPath.empty()) {
+        LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
+        return -EINVAL;
+    }
+    if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
+        LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
+        return err;
+    }
+    if (params.size > 0) {
+        // Only v2+ incfs supports automatically trimming file over-reserved sizes
+        if (mIncFs->features() & incfs::Features::v2) {
+            if (auto err = mIncFs->reserveSpace(ifs->control, normPath, params.size)) {
+                if (err != -EOPNOTSUPP) {
+                    LOG(ERROR) << "Failed to reserve space for a new file: " << err;
+                    (void)mIncFs->unlink(ifs->control, normPath);
+                    return err;
+                } else {
+                    LOG(WARNING) << "Reserving space for backing file isn't supported, "
+                                    "may run out of disk later";
+                }
+            }
         }
         if (!data.empty()) {
             if (auto err = setFileContent(ifs, id, path, data); err) {
+                (void)mIncFs->unlink(ifs->control, normPath);
                 return err;
             }
         }
-        return 0;
     }
-    return -EINVAL;
+    return 0;
 }
 
 int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
@@ -1708,7 +1740,7 @@
         }
 
         const auto entryUncompressed = entry.method == kCompressStored;
-        const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0;
+        const auto entryPageAligned = isPageAligned(entry.offset);
 
         if (!extractNativeLibs) {
             // ensure the file is properly aligned and unpacked
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 80f409f..2a06122 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -209,6 +209,10 @@
     ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
         return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
     }
+    ErrorCode reserveSpace(const Control& control, std::string_view path,
+                           IncFsSize size) const final {
+        return incfs::reserveSpace(control, path, size);
+    }
     WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
                                    std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
         return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d113f99..231b76f 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -107,6 +107,8 @@
     virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
     virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0;
     virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0;
+    virtual ErrorCode reserveSpace(const Control& control, std::string_view path,
+                                   IncFsSize size) const = 0;
     virtual WaitResult waitForPendingReads(
             const Control& control, std::chrono::milliseconds timeout,
             std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index bf798273..45b796b 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -372,6 +372,8 @@
     MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
     MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id));
     MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
+    MOCK_CONST_METHOD3(reserveSpace,
+                       ErrorCode(const Control& control, std::string_view path, IncFsSize size));
     MOCK_CONST_METHOD3(waitForPendingReads,
                        WaitResult(const Control& control, std::chrono::milliseconds timeout,
                                   std::vector<incfs::ReadInfo>* pendingReadsBuffer));
@@ -379,7 +381,10 @@
                        ErrorCode(const Control& control,
                                  const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
 
-    MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); }
+    MockIncFs() {
+        ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());
+        ON_CALL(*this, reserveSpace(_, _, _)).WillByDefault(Return(0));
+    }
 
     void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
     void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4e23609..6f71e99 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -390,8 +390,6 @@
     private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
     private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map";
 
-    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
     // maximum number of binder threads used for system_server
     // will be higher than the system default
     private static final int sMaxBinderThreads = 31;
@@ -1327,7 +1325,7 @@
                 false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
-        boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+        boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
 
         boolean isWatch = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WATCH);
@@ -1662,8 +1660,7 @@
             t.traceEnd();
 
             final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
-            final boolean hasGsi = SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
-            if (hasPdb && !hasGsi) {
+            if (hasPdb) {
                 t.traceBegin("StartPersistentDataBlock");
                 mSystemServiceManager.startService(PersistentDataBlockService.class);
                 t.traceEnd();
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 334e53a..988c02b 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -23,14 +23,17 @@
 
 android_test {
     name: "PackageManagerServiceUnitTests",
-    srcs: ["src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     static_libs: [
         "androidx.test.rules",
         "androidx.test.runner",
         "junit",
+        "kotlin-test",
         "services.core",
         "servicestests-utils",
-        "testng",
         "truth-prebuilt",
     ],
     platform_apis: true,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 0fa9a1d..d5eda20 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -25,8 +25,8 @@
 import android.util.ArraySet
 import com.android.server.SystemConfig
 import com.android.server.compat.PlatformCompat
-import com.android.server.pm.verify.domain.DomainVerificationCollector
 import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationCollector
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
index 8ef9239..9693f3b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -19,6 +19,7 @@
 import android.content.pm.verify.domain.DomainSet
 import android.content.pm.verify.domain.DomainVerificationInfo
 import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationState
 import android.content.pm.verify.domain.DomainVerificationUserState
 import android.os.Parcel
 import android.os.Parcelable
@@ -74,7 +75,9 @@
                     DomainVerificationInfo(
                         UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
                         "com.test.pkg",
-                        massiveSet.withIndex().associate { it.value to it.index }
+                        massiveSet.withIndex().associate {
+                            it.value to DomainVerificationState.convertToInfoState(it.index)
+                        }
                     )
                 },
                 unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 53f0ca2..7e25901 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -20,20 +20,21 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.PackageUserState
-import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.parsing.component.ParsedActivity
 import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Build
 import android.os.Process
 import android.util.ArraySet
 import android.util.SparseArray
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.verify.domain.DomainVerificationEnforcer
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
-import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.spyThrowOnUnmocked
 import com.android.server.testutils.whenever
@@ -50,6 +51,8 @@
 import java.util.UUID
 import java.util.concurrent.atomic.AtomicBoolean
 import java.util.concurrent.atomic.AtomicInteger
+import kotlin.test.assertFailsWith
+import kotlin.test.fail
 
 @RunWith(Parameterized::class)
 class DomainVerificationEnforcerTest {
@@ -81,47 +84,49 @@
                         whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
                             true
                         }
+                        whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
                     })
                 }
             }
 
-            val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger, DomainVerificationService> =
-                {
-                    val callingUidInt = AtomicInteger(-1)
-                    val callingUserIdInt = AtomicInteger(-1)
+            val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger,
+                    DomainVerificationService> = {
+                val callingUidInt = AtomicInteger(-1)
+                val callingUserIdInt = AtomicInteger(-1)
 
-                    val connection: DomainVerificationManagerInternal.Connection =
-                        mockThrowOnUnmocked {
-                            whenever(callingUid) { callingUidInt.get() }
-                            whenever(callingUserId) { callingUserIdInt.get() }
-                            whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
-                            whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
-                            whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
-                            whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
-                            whenever(schedule(anyInt(), any()))
-                            whenever(scheduleWriteSettings())
-                            whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
-                            whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
-                                true
-                            }
+                val connection: DomainVerificationManagerInternal.Connection =
+                    mockThrowOnUnmocked {
+                        whenever(callingUid) { callingUidInt.get() }
+                        whenever(callingUserId) { callingUserIdInt.get() }
+                        whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
+                        whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
+                        whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
+                        whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
+                        whenever(schedule(anyInt(), any()))
+                        whenever(scheduleWriteSettings())
+                        whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+                        whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+                            true
                         }
-                    val service = DomainVerificationService(
-                        it,
-                        mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
-                        mockThrowOnUnmocked {
-                            whenever(
-                                isChangeEnabled(
-                                    anyLong(),
-                                    any()
-                                )
-                            ) { true }
-                        }).apply {
-                        setConnection(connection)
+                        whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
                     }
-
-                    Triple(callingUidInt, callingUserIdInt, service)
+                val service = DomainVerificationService(
+                    it,
+                    mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+                    mockThrowOnUnmocked {
+                        whenever(
+                            isChangeEnabled(
+                                anyLong(),
+                                any()
+                            )
+                        ) { true }
+                    }).apply {
+                    setConnection(connection)
                 }
 
+                Triple(callingUidInt, callingUserIdInt, service)
+            }
+
             fun enforcer(
                 type: Type,
                 name: String,
@@ -175,7 +180,7 @@
                 service(Type.INTERNAL, "setStatusInternalPackageName") {
                     setDomainVerificationStatusInternal(
                         it.targetPackageName,
-                        DomainVerificationManager.STATE_SUCCESS,
+                        DomainVerificationState.STATE_SUCCESS,
                         ArraySet(setOf("example.com"))
                     )
                 },
@@ -206,7 +211,7 @@
                     setDomainVerificationStatus(
                         it.targetDomainSetId,
                         setOf("example.com"),
-                        DomainVerificationManager.STATE_SUCCESS
+                        DomainVerificationState.STATE_SUCCESS
                     )
                 },
                 service(Type.VERIFIER, "setStatusInternalUid") {
@@ -214,7 +219,7 @@
                         it.callingUid,
                         it.targetDomainSetId,
                         setOf("example.com"),
-                        DomainVerificationManager.STATE_SUCCESS
+                        DomainVerificationState.STATE_SUCCESS
                     )
                 },
                 service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
@@ -475,24 +480,7 @@
         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
             // User selector makes no distinction by UID
             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
-            if (throws) {
-                allUids.forEach {
-                    assertFails {
-                        runMethod(target, it, visible = true, callingUserId, targetUserId)
-                    }
-                }
-            } else {
-                allUids.forEach {
-                    runMethod(target, it, visible = true, callingUserId, targetUserId)
-                }
-            }
-
-            // User selector doesn't use QUERY_ALL, so the invisible package should always fail
-            allUids.forEach {
-                assertFails {
-                    runMethod(target, it, visible = false, callingUserId, targetUserId)
-                }
-            }
+            runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
         }
 
         val callingUserId = 0
@@ -529,24 +517,7 @@
         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
             // User selector makes no distinction by UID
             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
-            if (throws) {
-                allUids.forEach {
-                    assertFails {
-                        runMethod(target, it, visible = true, callingUserId, targetUserId)
-                    }
-                }
-            } else {
-                allUids.forEach {
-                    runMethod(target, it, visible = true, callingUserId, targetUserId)
-                }
-            }
-
-            // User selector doesn't use QUERY_ALL, so the invisible package should always fail
-            allUids.forEach {
-                assertFails {
-                    runMethod(target, it, visible = false, callingUserId, targetUserId)
-                }
-            }
+            runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
         }
 
         val callingUserId = 0
@@ -590,24 +561,10 @@
         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
             // Legacy makes no distinction by UID
             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
-            if (throws) {
-                allUids.forEach {
-                    assertFails {
-                        runMethod(target, it, visible = true, callingUserId, targetUserId)
-                    }
-                }
-            } else {
-                allUids.forEach {
-                    runMethod(target, it, visible = true, callingUserId, targetUserId)
-                }
-            }
-
-            // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
-            allUids.forEach {
-                assertFails {
-                    runMethod(target, it, visible = false, callingUserId, targetUserId)
-                }
-            }
+            // The legacy selector does a silent failure when the user IDs don't match, so it
+            // cannot verify the non-existent user ID check, as it will not throw an Exception.
+            runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+                verifyUserIdCheck = false)
         }
 
         val callingUserId = 0
@@ -642,27 +599,29 @@
         }
         val target = params.construct(context)
 
+        // Legacy code can return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+        // as an error code. This is distinct from the class level assertFails as unfortunately
+        // the same number, 0, is used in opposite contexts, where it does represent a failure
+        // for this legacy case, but not for the modern APIs.
+        fun assertFailsLegacy(block: () -> Any?) {
+            try {
+                val value = block()
+                if ((value as? Int)
+                        != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+                ) {
+                    throw AssertionError("Expected call to return false, was $value")
+                }
+            } catch (e: SecurityException) {
+            } catch (e: PackageManager.NameNotFoundException) {
+                // Any of these 2 exceptions are considered failures, which is expected
+            }
+        }
+
         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
             // Legacy makes no distinction by UID
             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
-            if (throws) {
-                allUids.forEach {
-                    assertFails {
-                        runMethod(target, it, visible = true, callingUserId, targetUserId)
-                    }
-                }
-            } else {
-                allUids.forEach {
-                    runMethod(target, it, visible = true, callingUserId, targetUserId)
-                }
-            }
-
-            // Legacy doesn't use QUERY_ALL, so the invisible package should always fail
-            allUids.forEach {
-                assertFails {
-                    runMethod(target, it, visible = false, callingUserId, targetUserId)
-                }
-            }
+            runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+                    assertFailsMethod = ::assertFailsLegacy)
         }
 
         val callingUserId = 0
@@ -704,17 +663,8 @@
         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
             // Owner querent makes no distinction by UID
             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
-            if (throws) {
-                allUids.forEach {
-                    assertFails {
-                        runMethod(target, it, visible = true, callingUserId, targetUserId)
-                    }
-                }
-            } else {
-                allUids.forEach {
-                    runMethod(target, it, visible = true, callingUserId, targetUserId)
-                }
-            }
+            runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
+                verifyInvisiblePkg = false)
         }
 
         val callingUserId = 0
@@ -782,22 +732,88 @@
         return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
     }
 
+    private fun runCrossUserMethod(
+        allUids: Iterable<Int>,
+        target: Any,
+        callingUserId: Int,
+        targetUserId: Int,
+        throws: Boolean,
+        verifyUserIdCheck: Boolean = true,
+        verifyInvisiblePkg: Boolean = true,
+        assertFailsMethod: (() -> Any?) -> Unit = ::assertFails,
+    ) {
+        if (throws) {
+            allUids.forEach {
+                assertFailsMethod {
+                    // When testing a non-user ID failure, send an invalid user ID.
+                    // This ensures the failure occurs before the user ID check is run.
+                    try {
+                        runMethod(target, it, visible = true, callingUserId, 100)
+                    } catch (e: SecurityException) {
+                        if (verifyUserIdCheck) {
+                            e.message?.let {
+                                if (it.contains("user ID", ignoreCase = true)
+                                    || it.contains("100")) {
+                                    fail(
+                                        "Method should not check user existence before permissions"
+                                    )
+                                }
+                            }
+                        }
+
+                        // Rethrow to allow normal fail checking logic to run
+                        throw e
+                    }
+                }
+            }
+        } else {
+            allUids.forEach {
+                runMethod(target, it, visible = true, callingUserId, targetUserId)
+            }
+        }
+
+        if (verifyInvisiblePkg) {
+            allUids.forEach {
+                assertFailsMethod {
+                    runMethod(target, it, visible = false, callingUserId, targetUserId)
+                }
+            }
+        }
+
+        if (verifyUserIdCheck) {
+            // An invalid target user ID should always fail
+            allUids.forEach {
+                assertFailsWith(SecurityException::class) {
+                    runMethod(target, it, visible = true, callingUserId, 100)
+                }
+            }
+
+            // An invalid calling user ID should always fail, although this cannot happen in prod
+            allUids.forEach {
+                assertFailsWith(SecurityException::class) {
+                    runMethod(target, it, visible = true, 100, targetUserId)
+                }
+            }
+        }
+    }
+
     private fun assertFails(block: () -> Any?) {
         try {
             val value = block()
-            // Some methods return false rather than throwing, so check that as well
-            if ((value as? Boolean) != false) {
-                // Can also return default value if it's a legacy call
-                if ((value as? Int)
-                    != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
-                ) {
-                    throw AssertionError("Expected call to return false, was $value")
-                }
+            // Some methods return false or an error rather than throwing, so check that as well
+            val valueAsBoolean = value as? Boolean
+            if (valueAsBoolean == false) {
+                // Expected failure, do not throw
+                return
+            }
+
+            val valueAsInt = value as? Int
+            if (valueAsInt != null && valueAsInt == DomainVerificationManager.STATUS_OK) {
+                throw AssertionError("Expected call to return false, was $value")
             }
         } catch (e: SecurityException) {
         } catch (e: PackageManager.NameNotFoundException) {
-        } catch (e: DomainVerificationManager.InvalidDomainSetException) {
-            // Any of these 3 exceptions are considered failures, which is expected
+            // Any of these 2 exceptions are considered failures, which is expected
         }
     }
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
new file mode 100644
index 0000000..8ae4c5a
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+
+import com.android.server.pm.verify.domain.DomainVerificationService;
+
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Proxies Kotlin calls to the Java layer such that null values can be passed for {@link NonNull}
+ * marked parameters, as Kotlin disallows this at the compiler leveling, preventing the null error
+ * codes from being tested.
+ */
+class DomainVerificationJavaUtil {
+
+    static int setStatusForceNullable(@NonNull DomainVerificationService service,
+            @Nullable UUID domainSetId, @Nullable Set<String> domains, int state)
+            throws PackageManager.NameNotFoundException {
+        return service.setDomainVerificationStatus(domainSetId, domains, state);
+    }
+
+    static int setUserSelectionForceNullable(@NonNull DomainVerificationService service,
+            @Nullable UUID domainSetId, @Nullable Set<String> domains, boolean enabled,
+            @UserIdInt int userId)
+            throws PackageManager.NameNotFoundException {
+        return service.setDomainVerificationUserSelection(domainSetId, domains, enabled, userId);
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
index 9a3bd99..509824d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
@@ -19,9 +19,9 @@
 import android.content.pm.IntentFilterVerificationInfo
 import android.content.pm.PackageManager
 import android.util.ArraySet
-import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
 import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.readXml
 import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.writeXml
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Rule
 import org.junit.Test
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
new file mode 100644
index 0000000..0e74b65
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationUserState
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+import kotlin.test.assertFailsWith
+
+class DomainVerificationManagerApiTest {
+
+    companion object {
+        private const val PKG_ONE = "com.test.one"
+        private const val PKG_TWO = "com.test.two"
+        private const val PKG_THREE = "com.test.three"
+
+        private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+        private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+        private val UUID_THREE = UUID.fromString("0b3260ed-07c4-4b45-840b-237f8fb8b433")
+        private val UUID_INVALID = UUID.fromString("ad33babc-490b-4965-9d78-7e91248b00f")
+
+        private val DOMAIN_BASE = DomainVerificationManagerApiTest::class.java.packageName
+        private val DOMAIN_1 = "one.$DOMAIN_BASE"
+        private val DOMAIN_2 = "two.$DOMAIN_BASE"
+        private val DOMAIN_3 = "three.$DOMAIN_BASE"
+        private val DOMAIN_4 = "four.$DOMAIN_BASE"
+    }
+
+    @Test
+    fun queryValidVerificationPackageNames() {
+        val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+        val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+            addPackages(pkgWithDomains, pkgWithoutDomains)
+        }
+
+        assertThat(service.queryValidVerificationPackageNames())
+                .containsExactly(pkgWithDomains.getName())
+    }
+
+    @Test
+    fun getDomainVerificationInfoId() {
+        val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+        val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+            addPackages(pkgWithDomains, pkgWithoutDomains)
+        }
+
+        assertThat(service.getDomainVerificationInfoId(PKG_ONE)).isEqualTo(UUID_ONE)
+        assertThat(service.getDomainVerificationInfoId(PKG_TWO)).isEqualTo(UUID_TWO)
+
+        assertThat(service.getDomainVerificationInfoId("invalid.pkg.name")).isEqualTo(null)
+    }
+
+    @Test
+    fun getDomainVerificationInfo() {
+        val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+        val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+            addPackages(pkgWithDomains, pkgWithoutDomains)
+        }
+
+        val infoOne = service.getDomainVerificationInfo(pkgWithDomains.getName())
+        assertThat(infoOne).isNotNull()
+        assertThat(infoOne!!.identifier).isEqualTo(pkgWithDomains.domainSetId)
+        assertThat(infoOne.packageName).isEqualTo(pkgWithDomains.getName())
+        assertThat(infoOne.hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DomainVerificationInfo.STATE_NO_RESPONSE,
+                DOMAIN_2 to DomainVerificationInfo.STATE_NO_RESPONSE,
+        ))
+
+        assertThat(service.getDomainVerificationInfo(pkgWithoutDomains.getName())).isNull()
+
+        assertFailsWith(PackageManager.NameNotFoundException::class) {
+            service.getDomainVerificationInfo("invalid.pkg.name")
+        }
+    }
+
+    @Test
+    fun setStatus() {
+        val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+
+        val map = mutableMapOf(pkg1.getName() to pkg1, pkg2.getName() to pkg2)
+        val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
+
+        assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_2), 1100))
+                .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+        assertThat(service.setStatus(UUID_INVALID, setOf(DOMAIN_1), 1100))
+                .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID)
+
+        assertThat(DomainVerificationJavaUtil.setStatusForceNullable(service, null,
+                setOf(DOMAIN_1), 1100))
+                .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL)
+
+        assertThat(DomainVerificationJavaUtil.setStatusForceNullable(service, UUID_ONE, null,
+                1100)).isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+        assertThat(service.setStatus(UUID_ONE, emptySet(), 1100))
+                .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+        assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_3), 1100))
+                .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+        assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_2, DOMAIN_3), 1100))
+                .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+        assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), 15))
+                .isEqualTo(DomainVerificationManager.ERROR_INVALID_STATE_CODE)
+
+        map.clear()
+        assertFailsWith(PackageManager.NameNotFoundException::class){
+            service.setStatus(UUID_ONE, setOf(DOMAIN_1), 1100)
+        }
+    }
+
+    @Test
+    fun setDomainVerificationLinkHandlingAllowed() {
+        val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+
+        val map = mutableMapOf(pkg1.getName() to pkg1, pkg2.getName() to pkg2)
+        val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
+
+        service.setDomainVerificationLinkHandlingAllowed(PKG_ONE, false, 0);
+
+        // Should edit same package, same user
+        assertThat(service.getDomainVerificationUserState(PKG_ONE, 0)
+                ?.isLinkHandlingAllowed).isEqualTo(false)
+
+        // Shouldn't edit different user
+        assertThat(service.getDomainVerificationUserState(PKG_ONE, 1)
+                ?.isLinkHandlingAllowed).isEqualTo(true)
+
+        // Shouldn't edit different package
+        assertThat(service.getDomainVerificationUserState(PKG_TWO, 0)
+                ?.isLinkHandlingAllowed).isEqualTo(true)
+
+        assertFailsWith(PackageManager.NameNotFoundException::class){
+            service.setDomainVerificationLinkHandlingAllowed("invalid.pkg.name", false, 0);
+        }
+    }
+
+    @Test
+    fun setUserSelection() {
+        val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+        val pkg3 = mockPkgSetting(PKG_THREE, UUID_THREE, listOf(DOMAIN_1, DOMAIN_2))
+
+        val map = mutableMapOf(
+                pkg1.getName() to pkg1,
+                pkg2.getName() to pkg2,
+                pkg3.getName() to pkg3
+        )
+        val service = makeService(map::get).apply { addPackages(pkg1, pkg2, pkg3) }
+
+        assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, 0))
+                .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+        assertThat(service.setUserSelection(UUID_INVALID, setOf(DOMAIN_1), true, 0))
+                .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID)
+
+        assertThat(DomainVerificationJavaUtil.setUserSelectionForceNullable(service, null,
+                setOf(DOMAIN_1), true, 0))
+                .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_ID_NULL)
+
+        assertThat(DomainVerificationJavaUtil.setUserSelectionForceNullable(service, UUID_ONE, null,
+                true, 0)).isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+        assertThat(service.setUserSelection(UUID_ONE, emptySet(), true, 0))
+                .isEqualTo(DomainVerificationManager.ERROR_DOMAIN_SET_NULL_OR_EMPTY)
+
+        assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_3), true, 0))
+                .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+        assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_1, DOMAIN_2, DOMAIN_3), true, 0))
+                .isEqualTo(DomainVerificationManager.ERROR_UNKNOWN_DOMAIN)
+
+        service.setStatus(UUID_ONE, setOf(DOMAIN_2), DomainVerificationInfo.STATE_SUCCESS)
+
+        assertThat(service.setUserSelection(UUID_THREE, setOf(DOMAIN_2), true, 0))
+            .isEqualTo(DomainVerificationManager.ERROR_UNABLE_TO_APPROVE)
+
+        map.clear()
+        assertFailsWith(PackageManager.NameNotFoundException::class){
+            service.setUserSelection(UUID_ONE, setOf(DOMAIN_1), true, 0)
+        }
+    }
+
+    @Test
+    fun getDomainVerificationUserState() {
+        val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+
+        val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
+            addPackages(pkgWithDomains, pkgWithoutDomains)
+        }
+
+        val infoOne = service.getDomainVerificationUserState(pkgWithDomains.getName(), 0)
+        assertThat(infoOne).isNotNull()
+        assertThat(infoOne!!.identifier).isEqualTo(pkgWithDomains.domainSetId)
+        assertThat(infoOne.packageName).isEqualTo(pkgWithDomains.getName())
+        assertThat(infoOne.isLinkHandlingAllowed).isTrue()
+        assertThat(infoOne.hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DomainVerificationUserState.DOMAIN_STATE_NONE,
+                DOMAIN_2 to DomainVerificationUserState.DOMAIN_STATE_NONE,
+        ))
+
+        val infoTwo = service.getDomainVerificationUserState(pkgWithoutDomains.getName(), 0)
+        assertThat(infoTwo).isNotNull()
+        assertThat(infoTwo!!.identifier).isEqualTo(pkgWithoutDomains.domainSetId)
+        assertThat(infoTwo.packageName).isEqualTo(pkgWithoutDomains.getName())
+        assertThat(infoOne.isLinkHandlingAllowed).isTrue()
+        assertThat(infoTwo.hostToStateMap).isEmpty()
+
+        assertFailsWith(PackageManager.NameNotFoundException::class) {
+            service.getDomainVerificationUserState("invalid.pkg.name", 0)
+        }
+    }
+
+    @Test
+    fun getOwnersForDomain() {
+        val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+        val service = makeService(pkg1, pkg2).apply {
+            addPackages(pkg1, pkg2)
+        }
+
+        assertThat(service.getOwnersForDomain(DOMAIN_1, 0)).isEmpty()
+
+        service.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
+
+        service.setStatus(pkg2.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
+
+        service.setUserSelection(pkg1.domainSetId, setOf(DOMAIN_2), true, 0)
+
+        service.getOwnersForDomain(DOMAIN_1, 0).let {
+            assertThat(it).hasSize(2)
+            assertThat(it[0].packageName).isEqualTo(pkg1.getName())
+            assertThat(it[0].isOverrideable).isEqualTo(false)
+            assertThat(it[1].packageName).isEqualTo(pkg2.getName())
+            assertThat(it[1].isOverrideable).isEqualTo(false)
+        }
+
+        service.getOwnersForDomain(DOMAIN_2, 0).let {
+            assertThat(it).hasSize(1)
+            assertThat(it.single().packageName).isEqualTo(pkg1.getName())
+            assertThat(it.single().isOverrideable).isEqualTo(true)
+        }
+        assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
+
+        service.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
+        service.getOwnersForDomain(DOMAIN_2, 0).let {
+            assertThat(it).hasSize(1)
+            assertThat(it.single().packageName).isEqualTo(pkg2.getName())
+            assertThat(it.single().isOverrideable).isEqualTo(true)
+        }
+        assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
+    }
+
+    private fun makeService(vararg pkgSettings: PackageSetting) =
+            makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } }
+
+    private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+            DomainVerificationService(mockThrowOnUnmocked {
+                // Assume the test has every permission necessary
+                whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+                whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+                    PackageManager.PERMISSION_GRANTED
+                }
+            }, mockThrowOnUnmocked {
+                whenever(linkedApps) { ArraySet<String>() }
+            }, mockThrowOnUnmocked {
+                whenever(isChangeEnabled(anyLong(), any())) { true }
+            }).apply {
+                setConnection(mockThrowOnUnmocked {
+                    whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+                    whenever(doesUserExist(0)) { true }
+                    whenever(doesUserExist(1)) { true }
+                    whenever(scheduleWriteSettings())
+
+                    // Need to provide an internal UID so some permission checks are ignored
+                    whenever(callingUid) { Process.ROOT_UID }
+                    whenever(callingUserId) { 0 }
+
+                    whenever(getPackageSettingLocked(anyString())) {
+                        pkgSettingFunction(arguments[0] as String)
+                    }
+                    whenever(getPackageLocked(anyString())) {
+                        pkgSettingFunction(arguments[0] as String)?.getPkg()
+                    }
+                })
+            }
+
+    private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
+            DOMAIN_1, DOMAIN_2
+    )) = mockThrowOnUnmocked<PackageSetting> {
+        val pkg = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { pkgName }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+            val activityList = listOf(
+                    ParsedActivity().apply {
+                        domains.forEach {
+                            addIntent(
+                                    ParsedIntentInfo().apply {
+                                        autoVerify = true
+                                        addAction(Intent.ACTION_VIEW)
+                                        addCategory(Intent.CATEGORY_BROWSABLE)
+                                        addCategory(Intent.CATEGORY_DEFAULT)
+                                        addDataScheme("http")
+                                        addDataScheme("https")
+                                        addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                        addDataAuthority(it, null)
+                                    }
+                            )
+                        }
+                    },
+            )
+
+            whenever(activities) { activityList }
+        }
+
+        whenever(getPkg()) { pkg }
+        whenever(getName()) { pkgName }
+        whenever(this.domainSetId) { domainSetId }
+        whenever(getInstantApp(anyInt())) { false }
+        whenever(firstInstallTime) { 0L }
+    }
+
+    fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
+            pkgSettings.forEach(::addPackage)
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
index 8c31c65..a5db3c5 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.pm.test.verify.domain
 
-import android.content.pm.verify.domain.DomainVerificationRequest
 import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationRequest
 import android.content.pm.verify.domain.DomainVerificationUserState
 import com.android.server.pm.verify.domain.DomainVerificationPersistence
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
new file mode 100644
index 0000000..fe3672d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
+import android.content.pm.verify.domain.DomainVerificationInfo.STATE_UNMODIFIABLE
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED
+import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import android.util.Xml
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+
+class DomainVerificationPackageTest {
+
+    companion object {
+        private const val PKG_ONE = "com.test.one"
+        private const val PKG_TWO = "com.test.two"
+        private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+        private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+
+        private val DOMAIN_BASE = DomainVerificationPackageTest::class.java.packageName
+        private val DOMAIN_1 = "one.$DOMAIN_BASE"
+        private val DOMAIN_2 = "two.$DOMAIN_BASE"
+        private val DOMAIN_3 = "three.$DOMAIN_BASE"
+        private val DOMAIN_4 = "four.$DOMAIN_BASE"
+
+        private const val USER_ID = 0
+    }
+
+    private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
+    private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
+
+    @Test
+    fun addPackageFirstTime() {
+        val service = makeService(pkg1, pkg2)
+        service.addPackage(pkg1)
+        val info = service.getInfo(pkg1.getName())
+        assertThat(info.packageName).isEqualTo(pkg1.getName())
+        assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+        assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_NO_RESPONSE,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+        ))
+
+        val userState = service.getUserState(pkg1.getName())
+        assertThat(userState.packageName).isEqualTo(pkg1.getName())
+        assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+        assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
+        assertThat(userState.user.identifier).isEqualTo(USER_ID)
+        assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_NONE,
+                DOMAIN_2 to DOMAIN_STATE_NONE,
+        ))
+
+        assertThat(service.queryValidVerificationPackageNames())
+                .containsExactly(pkg1.getName())
+    }
+
+    @Test
+    fun addPackageActive() {
+        // language=XML
+        val xml = """
+            <?xml?>
+            <domain-verifications>
+                <active>
+                    <package-state
+                        packageName="${pkg1.getName()}"
+                        id="${pkg1.domainSetId}"
+                        >
+                        <state>
+                            <domain name="$DOMAIN_1" state="$STATE_SUCCESS"/>
+                        </state>
+                        <user-states>
+                            <user-state userId="$USER_ID" allowLinkHandling="false">
+                                <enabled-hosts>
+                                    <host name="$DOMAIN_2"/>
+                                </enabled-hosts>
+                            </user-state>
+                        </user-states>
+                    </package-state>
+                </active>
+            </domain-verifications>
+        """.trimIndent()
+
+        val service = makeService(pkg1, pkg2)
+        xml.byteInputStream().use {
+            service.readSettings(Xml.resolvePullParser(it))
+        }
+
+        service.addPackage(pkg1)
+
+        val info = service.getInfo(pkg1.getName())
+        assertThat(info.packageName).isEqualTo(pkg1.getName())
+        assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+        assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_SUCCESS,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+        ))
+
+        val userState = service.getUserState(pkg1.getName())
+        assertThat(userState.packageName).isEqualTo(pkg1.getName())
+        assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+        assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
+        assertThat(userState.user.identifier).isEqualTo(USER_ID)
+        assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+                DOMAIN_2 to DOMAIN_STATE_SELECTED,
+        ))
+
+        assertThat(service.queryValidVerificationPackageNames())
+                .containsExactly(pkg1.getName())
+    }
+
+    @Test
+    fun migratePackageDropDomain() {
+        val pkgName = PKG_ONE
+        val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
+                listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
+        val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
+                listOf(DOMAIN_1, DOMAIN_2))
+
+        // Test 4 domains:
+        // 1 will be approved and preserved, 2 will be selected and preserved,
+        // 3 will be denied and dropped, 4 will be selected and dropped
+
+        val map = mutableMapOf<String, PackageSetting>()
+        val service = makeService { map[it] }
+        service.addPackage(pkgBefore)
+
+        // Only insert the package after addPackage call to ensure the service doesn't access
+        // a live package inside the addPackage logic. It should only use the provided input.
+        map[pkgName] = pkgBefore
+
+        // To test the approve/denial states, use the internal methods for this variant
+        service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_APPROVED,
+                ArraySet(setOf(DOMAIN_1)))
+        service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_DENIED,
+                ArraySet(setOf(DOMAIN_3)))
+        service.setUserSelection(
+                UUID_ONE, setOf(DOMAIN_2, DOMAIN_4), true, USER_ID)
+
+        // Check the verifier cannot change the shell approve/deny states
+        service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_3), STATE_SUCCESS)
+
+        assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_UNMODIFIABLE,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+                DOMAIN_3 to STATE_UNMODIFIABLE,
+                DOMAIN_4 to STATE_NO_RESPONSE,
+        ))
+        assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+                DOMAIN_2 to DOMAIN_STATE_SELECTED,
+                DOMAIN_3 to DOMAIN_STATE_NONE,
+                DOMAIN_4 to DOMAIN_STATE_SELECTED,
+        ))
+
+        // Now remove the package because migrateState shouldn't use it either
+        map.remove(pkgName)
+
+        map[pkgName] = pkgAfter
+
+        service.migrateState(pkgBefore, pkgAfter)
+
+        assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_UNMODIFIABLE,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+        ))
+        assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+                DOMAIN_2 to DOMAIN_STATE_SELECTED,
+        ))
+        assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+    }
+
+    @Test
+    fun migratePackageDropAll() {
+        val pkgName = PKG_ONE
+        val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+        val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, emptyList())
+
+        val map = mutableMapOf<String, PackageSetting>()
+        val service = makeService { map[it] }
+        service.addPackage(pkgBefore)
+
+        // Only insert the package after addPackage call to ensure the service doesn't access
+        // a live package inside the addPackage logic. It should only use the provided input.
+        map[pkgName] = pkgBefore
+
+        assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_NO_RESPONSE,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+        ))
+        assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_NONE,
+                DOMAIN_2 to DOMAIN_STATE_NONE,
+        ))
+        assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+
+        // Now remove the package because migrateState shouldn't use it either
+        map.remove(pkgName)
+
+        service.migrateState(pkgBefore, pkgAfter)
+
+        map[pkgName] = pkgAfter
+
+        assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+            .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+        assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+            .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+        assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
+        assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
+        assertThat(service.queryValidVerificationPackageNames()).isEmpty()
+    }
+
+    @Test
+    fun migratePackageAddDomain() {
+        val pkgName = PKG_ONE
+        val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
+                listOf(DOMAIN_1, DOMAIN_2))
+        val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
+                listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
+
+        // Test 3 domains:
+        // 1 will be verified and preserved, 2 will be selected and preserved,
+        // 3 will be new and default
+
+        val map = mutableMapOf<String, PackageSetting>()
+        val service = makeService { map[it] }
+        service.addPackage(pkgBefore)
+
+        // Only insert the package after addPackage call to ensure the service doesn't access
+        // a live package inside the addPackage logic. It should only use the provided input.
+        map[pkgName] = pkgBefore
+
+        service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS)
+        service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID)
+
+        assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_SUCCESS,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+        ))
+        assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+                DOMAIN_2 to DOMAIN_STATE_SELECTED,
+        ))
+
+        // Now remove the package because migrateState shouldn't use it either
+        map.remove(pkgName)
+
+        service.migrateState(pkgBefore, pkgAfter)
+
+        map[pkgName] = pkgAfter
+
+        assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_SUCCESS,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+                DOMAIN_3 to STATE_NO_RESPONSE,
+        ))
+        assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+                DOMAIN_2 to DOMAIN_STATE_SELECTED,
+                DOMAIN_3 to DOMAIN_STATE_NONE,
+        ))
+        assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+    }
+
+    @Test
+    fun migratePackageAddAll() {
+        val pkgName = PKG_ONE
+        val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, emptyList())
+        val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+        val map = mutableMapOf<String, PackageSetting>()
+        val service = makeService { map[it] }
+        service.addPackage(pkgBefore)
+
+        // Only insert the package after addPackage call to ensure the service doesn't access
+        // a live package inside the addPackage logic. It should only use the provided input.
+        map[pkgName] = pkgBefore
+
+        assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+            .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+        assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+            .isNotEqualTo(DomainVerificationManager.STATUS_OK)
+
+        assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
+        assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
+        assertThat(service.queryValidVerificationPackageNames()).isEmpty()
+
+        // Now remove the package because migrateState shouldn't use it either
+        map.remove(pkgName)
+
+        service.migrateState(pkgBefore, pkgAfter)
+
+        map[pkgName] = pkgAfter
+
+        assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to STATE_NO_RESPONSE,
+                DOMAIN_2 to STATE_NO_RESPONSE,
+        ))
+        assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+                DOMAIN_1 to DOMAIN_STATE_NONE,
+                DOMAIN_2 to DOMAIN_STATE_NONE,
+        ))
+        assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+    }
+
+    private fun DomainVerificationService.getInfo(pkgName: String) =
+            getDomainVerificationInfo(pkgName)
+                    .also { assertThat(it).isNotNull() }!!
+
+    private fun DomainVerificationService.getUserState(pkgName: String) =
+            getDomainVerificationUserState(pkgName, USER_ID)
+                    .also { assertThat(it).isNotNull() }!!
+
+    private fun makeService(vararg pkgSettings: PackageSetting) =
+            makeService { pkgName -> pkgSettings.find { pkgName == it.getName()} }
+
+    private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+            DomainVerificationService(mockThrowOnUnmocked {
+                // Assume the test has every permission necessary
+                whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+                whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+                    PackageManager.PERMISSION_GRANTED
+                }
+            }, mockThrowOnUnmocked {
+                whenever(linkedApps) { ArraySet<String>() }
+            }, mockThrowOnUnmocked {
+                whenever(isChangeEnabled(ArgumentMatchers.anyLong(), any())) { true }
+            }).apply {
+                setConnection(mockThrowOnUnmocked {
+                    whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+                    whenever(doesUserExist(0)) { true }
+                    whenever(doesUserExist(1)) { true }
+                    whenever(scheduleWriteSettings())
+
+                    // Need to provide an internal UID so some permission checks are ignored
+                    whenever(callingUid) { Process.ROOT_UID }
+                    whenever(callingUserId) { 0 }
+
+                    whenever(getPackageSettingLocked(anyString())) {
+                        pkgSettingFunction(arguments[0] as String)!!
+                    }
+                    whenever(getPackageLocked(anyString())) {
+                        pkgSettingFunction(arguments[0] as String)!!.getPkg()
+                    }
+                })
+            }
+
+    private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
+            DOMAIN_1, DOMAIN_2
+    )) = mockThrowOnUnmocked<PackageSetting> {
+        val pkg = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { pkgName }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+            val activityList = listOf(
+                    ParsedActivity().apply {
+                        domains.forEach {
+                            addIntent(
+                                    ParsedIntentInfo().apply {
+                                        autoVerify = true
+                                        addAction(Intent.ACTION_VIEW)
+                                        addCategory(Intent.CATEGORY_BROWSABLE)
+                                        addCategory(Intent.CATEGORY_DEFAULT)
+                                        addDataScheme("http")
+                                        addDataScheme("https")
+                                        addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                        addDataAuthority(it, null)
+                                    }
+                            )
+                        }
+                    },
+            )
+
+            whenever(activities) { activityList }
+        }
+
+        whenever(getPkg()) { pkg }
+        whenever(getName()) { pkgName }
+        whenever(this.domainSetId) { domainSetId }
+        whenever(getInstantApp(anyInt())) { false }
+        whenever(firstInstallTime) { 0L }
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
index 6597577..f8fda12 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.pm.test.verify.domain
 
-import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
 import android.util.ArrayMap
 import android.util.TypedXmlPullParser
 import android.util.TypedXmlSerializer
@@ -117,11 +117,11 @@
     @Test
     fun readMalformed() {
         val stateZero = mockEmptyPkgState(0).apply {
-            stateMap["example.com"] = DomainVerificationManager.STATE_SUCCESS
-            stateMap["example.org"] = DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED
+            stateMap["example.com"] = DomainVerificationState.STATE_SUCCESS
+            stateMap["example.org"] = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED
 
             // A domain without a written state falls back to default
-            stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
+            stateMap["missing-state.com"] = DomainVerificationState.STATE_NO_RESPONSE
 
             userStates[1] = DomainVerificationInternalUserState(1).apply {
                 addHosts(setOf("example-user1.com", "example-user1.org"))
@@ -159,9 +159,9 @@
                         >
                         <state>
                             <domain name="example.com" state="${
-                                DomainVerificationManager.STATE_SUCCESS}"/>
+                                DomainVerificationState.STATE_SUCCESS}"/>
                             <domain name="example.org" state="${
-                                DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
+                                DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED}"/>
                             <not-domain name="not-domain.com" state="1"/>
                             <domain name="missing-state.com"/>
                         </state>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
index 91e5bec..a9b77ea 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
@@ -21,20 +21,20 @@
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.pm.PackageManager
+import android.content.pm.verify.domain.DomainVerificationInfo
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.verify.domain.DomainVerificationRequest
-import android.content.pm.verify.domain.DomainVerificationInfo
 import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Bundle
 import android.os.UserHandle
 import android.util.ArraySet
 import com.android.server.DeviceIdleInternal
+import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.verify.domain.DomainVerificationCollector
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2
-import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
@@ -106,20 +106,22 @@
                 when (val pkgName = arguments[0] as String) {
                     TEST_PKG_NAME_TARGET_ONE -> DomainVerificationInfo(
                         TEST_UUID_ONE, pkgName, mapOf(
-                            "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE,
-                            "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE
+                            "example1.com" to DomainVerificationInfo.STATE_NO_RESPONSE,
+                            "example2.com" to DomainVerificationInfo.STATE_NO_RESPONSE
                         )
                     )
                     TEST_PKG_NAME_TARGET_TWO -> DomainVerificationInfo(
                         TEST_UUID_TWO, pkgName, mapOf(
-                            "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE,
-                            "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE
+                            "example3.com" to DomainVerificationInfo.STATE_NO_RESPONSE,
+                            "example4.com" to DomainVerificationInfo.STATE_NO_RESPONSE
                         )
                     )
                     else -> throw IllegalArgumentException("Unexpected package name $pkgName")
                 }
             }
-            whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt()))
+            whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt())) {
+                DomainVerificationManager.STATUS_OK
+            }
         }
         collector = mockThrowOnUnmocked {
             whenever(collectValidAutoVerifyDomains(any())) {
@@ -316,7 +318,7 @@
             eq(TEST_CALLING_UID_ACCEPT),
             idCaptor.capture(),
             hostCaptor.capture(),
-            eq(DomainVerificationManager.STATE_SUCCESS)
+            eq(DomainVerificationState.STATE_SUCCESS)
         )
 
         assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt
new file mode 100644
index 0000000..6859b11
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationServiceUtil.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.annotation.UserIdInt
+import com.android.server.pm.verify.domain.DomainVerificationService
+import java.util.UUID
+
+fun DomainVerificationService.setStatus(domainSetId: UUID, domains: Set<String>, state: Int) =
+    setDomainVerificationStatus(domainSetId, domains.toMutableSet(), state)
+
+fun DomainVerificationService.setUserSelection(
+    domainSetId: UUID,
+    domains: Set<String>,
+    enabled: Boolean,
+    @UserIdInt userId: Int
+) = setDomainVerificationUserSelection(domainSetId, domains.toMutableSet(), enabled, userId)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 0d8f275..377bae1 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -20,28 +20,27 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.PackageUserState
-import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.parsing.component.ParsedActivity
 import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Build
 import android.os.Process
 import android.util.ArraySet
 import android.util.SparseArray
 import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
-import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.spyThrowOnUnmocked
 import com.android.server.testutils.whenever
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import org.mockito.Mockito
+import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.anyLong
-import org.mockito.Mockito.any
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.verify
@@ -129,13 +128,13 @@
                     setDomainVerificationStatus(
                         TEST_UUID,
                         setOf("example.com"),
-                        DomainVerificationManager.STATE_SUCCESS
+                        DomainVerificationState.STATE_SUCCESS
                     )
                 },
                 service("setStatusInternalPackageName") {
                     setDomainVerificationStatusInternal(
                         TEST_PKG,
-                        DomainVerificationManager.STATE_SUCCESS,
+                        DomainVerificationState.STATE_SUCCESS,
                         ArraySet(setOf("example.com"))
                     )
                 },
@@ -144,7 +143,7 @@
                         TEST_UID,
                         TEST_UUID,
                         setOf("example.com"),
-                        DomainVerificationManager.STATE_SUCCESS
+                        DomainVerificationState.STATE_SUCCESS
                     )
                 },
                 service("setLinkHandlingAllowedUserId") {
@@ -266,5 +265,7 @@
 
             // This doesn't check for visibility; that's done in the enforcer test
             whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+            whenever(doesUserExist(0)) { true }
+            whenever(doesUserExist(10)) { true }
         }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 0576125..44c1b8f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -21,6 +21,7 @@
 import android.content.pm.parsing.component.ParsedActivity
 import android.content.pm.parsing.component.ParsedIntentInfo
 import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationState
 import android.content.pm.verify.domain.DomainVerificationUserState
 import android.os.Build
 import android.os.PatternMatcher
@@ -74,6 +75,8 @@
         }).apply {
             setConnection(mockThrowOnUnmocked {
                 whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+                whenever(doesUserExist(0)) { true }
+                whenever(doesUserExist(1)) { true }
                 whenever(scheduleWriteSettings())
 
                 // Need to provide an internal UID so some permission checks are ignored
@@ -154,19 +157,20 @@
             .containsExactly(PKG_TWO)
     }
 
-    @Test(expected = IllegalArgumentException::class)
+    @Test
     fun anotherPackageTakeoverFailure() {
         val service = makeService()
 
         // Verify 1 to give it a higher approval level
         service.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
-            DomainVerificationManager.STATE_SUCCESS)
+            DomainVerificationState.STATE_SUCCESS)
         assertThat(service.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
         assertThat(service.getOwnersForDomain(DOMAIN_ONE, USER_ID).map { it.packageName })
             .containsExactly(PKG_ONE)
 
         // Attempt override by package 2
-        service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true, USER_ID)
+        assertThat(service.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true,
+                USER_ID)).isEqualTo(DomainVerificationManager.ERROR_UNABLE_TO_APPROVE)
     }
 
     private fun DomainVerificationService.stateFor(pkgName: String, host: String) =
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
similarity index 84%
rename from services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
rename to services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index ee0a16a..2e0cadf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.pm.dex;
@@ -28,28 +28,34 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.BatteryManager;
 import android.os.Build;
+import android.os.PowerManager;
 import android.os.UserHandle;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.server.pm.Installer;
+import com.android.server.pm.PackageManagerService;
 
 import dalvik.system.DelegateLastClassLoader;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
 
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
 
 import java.io.File;
@@ -69,9 +75,15 @@
             DelegateLastClassLoader.class.getName();
     private static final String UNSUPPORTED_CLASS_LOADER_NAME = "unsupported.class_loader";
 
-    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+    private static final int TEST_BATTERY_LEVEL_CRITICAL = 10;
+    private static final int TEST_BATTERY_LEVEL_DEFAULT = 80;
+
+    public StaticMockitoSession mMockitoSession;
     @Mock Installer mInstaller;
     @Mock IPackageManager mPM;
+    @Mock BatteryManager mMockBatteryManager;
+    @Mock PowerManager mMockPowerManager;
+
     private final Object mInstallLock = new Object();
 
     private DexManager mDexManager;
@@ -117,7 +129,37 @@
         mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0,
                 DELEGATE_LAST_CLASS_LOADER_NAME);
 
-        mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
+        // Initialize Static Mocking
+
+        mMockitoSession = ExtendedMockito.mockitoSession()
+            .initMocks(this)
+            .strictness(Strictness.LENIENT)
+            .startMocking();
+
+        // Mock....
+
+        mMockBatteryManager = ExtendedMockito.mock(BatteryManager.class);
+        mMockPowerManager   = ExtendedMockito.mock(PowerManager.class);
+
+        setDefaultMockValues();
+
+        Resources mockResources = ExtendedMockito.mock(Resources.class);
+        ExtendedMockito.when(mockResources
+            .getInteger(com.android.internal.R.integer.config_criticalBatteryWarningLevel))
+                .thenReturn(15);
+
+        Context mockContext = ExtendedMockito.mock(Context.class);
+        ExtendedMockito.doReturn(mockResources)
+            .when(mockContext)
+                .getResources();
+        ExtendedMockito.doReturn(mMockBatteryManager)
+            .when(mockContext)
+                .getSystemService(BatteryManager.class);
+        ExtendedMockito.doReturn(mMockPowerManager)
+            .when(mockContext)
+                .getSystemService(PowerManager.class);
+
+        mDexManager = new DexManager(mockContext, mPM, /*PackageDexOptimizer*/ null,
                 mInstaller, mInstallLock);
 
         // Foo and Bar are available to user0.
@@ -128,6 +170,25 @@
         mDexManager.load(existingPackages);
     }
 
+    @After
+    public void teardown() throws Exception {
+        mMockitoSession.finishMocking();
+    }
+
+    private void setDefaultMockValues() {
+        ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_DISCHARGING)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+        ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_DEFAULT)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+        ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_NONE)
+            .when(mMockPowerManager)
+                .getCurrentThermalStatus();
+    }
+
     @Test
     public void testNotifyPrimaryUse() {
         // The main dex file and splits are re-loaded by the app.
@@ -633,6 +694,114 @@
         assertNoDclInfo(mSystemServerJarInvalid);
     }
 
+    @Test
+    public void testInstallScenarioToReasonDefault() {
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
+    @Test
+    public void testInstallScenarioToReasonThermal() {
+        ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_SEVERE)
+            .when(mMockPowerManager)
+                .getCurrentThermalStatus();
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
+    @Test
+    public void testInstallScenarioToReasonBatteryDischarging() {
+        ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
+    @Test
+    public void testInstallScenarioToReasonBatteryCharging() {
+        ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+        ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_CHARGING)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
             String[] expectedContexts) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
new file mode 100644
index 0000000..5a4431e
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
@@ -0,0 +1,2 @@
+calin@google.com
+ngeoffray@google.com
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/OwnersTest/OWNERS b/services/tests/servicestests/assets/OwnersTest/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 44b9f44..872b955 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1114,6 +1114,30 @@
                 argThat(closeTo(newEndSpec)));
     }
 
+    @Test
+    public void testSetScale_toMagnifying_shouldNotifyActivatedState() {
+        setScaleToMagnifying();
+
+        verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(true));
+    }
+
+    @Test
+    public void testReset_afterMagnifying_shouldNotifyDeactivatedState() {
+        setScaleToMagnifying();
+
+        mFullScreenMagnificationController.reset(DISPLAY_0, mAnimationCallback);
+        verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false));
+    }
+
+    private void setScaleToMagnifying() {
+        register(DISPLAY_0);
+        float scale = 2.0f;
+        PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+
+        mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y,
+                false, SERVICE_ID_1);
+    }
+
     private void initMockWindowManager() {
         for (int i = 0; i < DISPLAY_COUNT; i++) {
             when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 0c3640c..84c76b7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.accessibility.magnification;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
 import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
 
 import static org.junit.Assert.assertEquals;
@@ -24,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
@@ -283,6 +287,27 @@
     }
 
     @Test
+    public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() {
+        mMagnificationController.onWindowMagnificationActivationState(true);
+
+        mMagnificationController.onWindowMagnificationActivationState(false);
+
+        verify(mMagnificationController).logMagnificationUsageState(
+                eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), anyLong());
+    }
+
+    @Test
+    public void
+            onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() {
+        mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+        mMagnificationController.onFullScreenMagnificationActivationState(false);
+
+        verify(mMagnificationController).logMagnificationUsageState(
+                eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), anyLong());
+    }
+
+    @Test
     public void onTouchInteractionStart_fullScreenAndCapabilitiesAll_showMagnificationButton()
             throws RemoteException {
         setMagnificationEnabled(MODE_FULLSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index f26c86c..955217c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -392,6 +392,23 @@
         assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
     }
 
+    @Test
+    public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+        verify(mMockCallback).onWindowMagnificationActivationState(eq(true));
+    }
+
+    @Test
+    public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, true);
+
+        verify(mMockCallback).onWindowMagnificationActivationState(eq(false));
+    }
+
     private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
         final int len = pointersLocation.length;
 
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index 8d54ead..73a2feb 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -17,8 +17,10 @@
 package com.android.server.am;
 
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT;
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -39,6 +41,7 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.PowerProfile;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,7 +64,7 @@
     public void setUp() {
         final Context context = InstrumentationRegistry.getContext();
 
-        mBatteryStatsImpl = new TestBatteryStatsImpl();
+        mBatteryStatsImpl = new TestBatteryStatsImpl(context);
         mPowerStatsInternal = new TestPowerStatsInternal();
         mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context),
                 mBatteryStatsImpl);
@@ -72,13 +75,24 @@
         final int numCpuClusters = 4;
         final int numOther = 3;
 
-        final IntArray tempAllIds = new IntArray();
         // Add some energy consumers used by BatteryExternalStatsWorker.
+        final IntArray tempAllIds = new IntArray();
+
         final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
                 "display");
         tempAllIds.add(displayId);
         mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
 
+        final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0,
+                "wifi");
+        tempAllIds.add(wifiId);
+        mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456);
+
+        final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0,
+                "bt");
+        tempAllIds.add(btId);
+        mPowerStatsInternal.incrementEnergyConsumption(btId, 34567);
+
         final int[] cpuClusterIds = new int[numCpuClusters];
         for (int i = 0; i < numCpuClusters; i++) {
             cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
@@ -109,6 +123,18 @@
         assertEquals(1, displayResults.length);
         assertEquals(displayId, displayResults[0].id);
 
+        final EnergyConsumerResult[] wifiResults =
+                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null);
+        // Results should only have the wifi energy consumer
+        assertEquals(1, wifiResults.length);
+        assertEquals(wifiId, wifiResults[0].id);
+
+        final EnergyConsumerResult[] bluetoothResults =
+                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_BT).getNow(null);
+        // Results should only have the bluetooth energy consumer
+        assertEquals(1, bluetoothResults.length);
+        assertEquals(btId, bluetoothResults[0].id);
+
         final EnergyConsumerResult[] cpuResults =
                 mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
         // Results should only have the cpu cluster energy consumers
@@ -148,6 +174,9 @@
     }
 
     public class TestBatteryStatsImpl extends BatteryStatsImpl {
+        public TestBatteryStatsImpl(Context context) {
+            mPowerProfile = new PowerProfile(context, true /* forTest */);
+        }
     }
 
     public class TestPowerStatsInternal extends PowerStatsInternal {
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index c1b6101..bf621b1 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -30,11 +30,13 @@
 import android.hardware.SensorManager;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -43,6 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class AutomaticBrightnessControllerTest {
     private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
@@ -55,9 +58,11 @@
     private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
     private static final int DISPLAY_ID = 0;
     private static final int LAYER_STACK = 0;
+    private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
 
     private Context mContext;
     private LogicalDisplay mLogicalDisplay;
+    private AutomaticBrightnessController mController;
 
     @Mock SensorManager mSensorManager;
     @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
@@ -67,7 +72,6 @@
     @Mock DisplayDevice mDisplayDevice;
     @Mock HighBrightnessModeController mHbmController;
 
-    private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
     @Before
     public void setUp() {
         // Share classloader to allow package private access.
@@ -78,6 +82,15 @@
         mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
     }
 
+    @After
+    public void tearDown() {
+        if (mController != null) {
+            // Stop the update Brightness loop.
+            mController.stop();
+            mController = null;
+        }
+    }
+
     private AutomaticBrightnessController setupController(Sensor lightSensor) {
         AutomaticBrightnessController controller = new AutomaticBrightnessController(
                 new AutomaticBrightnessController.Injector() {
@@ -94,7 +107,9 @@
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay,
                 mContext, mHbmController
         );
-        controller.setLoggingEnabled(true);
+
+        when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT);
 
         // Configure the brightness controller and grab an instance of the sensor listener,
         // through which we can deliver fake (for test) sensor values.
@@ -108,7 +123,7 @@
     @Test
     public void testNoHysteresisAtMinBrightness() throws Exception {
         Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
-        AutomaticBrightnessController controller = setupController(lightSensor);
+        mController = setupController(lightSensor);
 
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
@@ -135,7 +150,7 @@
 
         // Send new sensor value and verify
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
-        assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f);
+        assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f);
 
         // Set up system to return 0.0f (minimum possible brightness) as a brightness value
         float lux2 = 10.0f;
@@ -149,13 +164,13 @@
 
         // Send new sensor value and verify
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
-        assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f);
+        assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f);
     }
 
     @Test
     public void testNoHysteresisAtMaxBrightness() throws Exception {
         Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
-        AutomaticBrightnessController controller = setupController(lightSensor);
+        mController = setupController(lightSensor);
 
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
@@ -181,7 +196,7 @@
 
         // Send new sensor value and verify
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
-        assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f);
+        assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f);
 
 
         // Set up system to return 1.0f as a brightness value (brightness_max)
@@ -196,13 +211,13 @@
 
         // Send new sensor value and verify
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
-        assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f);
+        assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f);
     }
 
     @Test
     public void testUserAddUserDataPoint() throws Exception {
         Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
-        AutomaticBrightnessController controller = setupController(lightSensor);
+        mController = setupController(lightSensor);
 
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
@@ -214,7 +229,7 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000));
 
         // User sets brightness to 100
-        controller.configure(true /* enable */, null /* configuration */,
+        mController.configure(true /* enable */, null /* configuration */,
                 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */,
                 false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index a38745f..d9af51f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -42,7 +42,6 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.recovery.KeyChainSnapshot;
@@ -109,7 +108,7 @@
     private RecoverySnapshotStorage mRecoverySnapshotStorage;
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
     private File mDatabaseFile;
-    private AndroidKeyStoreSecretKey mWrappingKey;
+    private SecretKey mWrappingKey;
     private PlatformEncryptionKey mEncryptKey;
 
     private KeySyncTask mKeySyncTask;
@@ -848,7 +847,7 @@
         return keyGenerator.generateKey();
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -857,7 +856,7 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 
     private static byte[] utf8Bytes(String s) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index c295177..6413026 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertNull;
 
 import android.content.Context;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 
@@ -45,6 +44,7 @@
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 import javax.crypto.spec.GCMParameterSpec;
 
 @SmallTest
@@ -77,7 +77,7 @@
         mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
         mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
 
-        AndroidKeyStoreSecretKey platformKey = generatePlatformKey();
+        SecretKey platformKey = generatePlatformKey();
         mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
         mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
         mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
@@ -168,7 +168,7 @@
         assertArrayEquals(rawMaterial, unwrappedMaterial);
     }
 
-    private AndroidKeyStoreSecretKey generatePlatformKey() throws Exception {
+    private SecretKey generatePlatformKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -177,7 +177,7 @@
                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                     .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 
     private static byte[] randomBytes(int n) {
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 b65e487..a227cd3 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
@@ -45,7 +45,6 @@
 import android.os.Binder;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -1311,7 +1310,7 @@
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -1320,6 +1319,6 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index 9813ab7..60052f7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.util.Pair;
@@ -117,7 +116,7 @@
     @Test
     public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception {
         String alias = "karlin";
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -136,7 +135,7 @@
     @Test
     public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception {
         String alias = "karlin";
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA);
@@ -155,7 +154,7 @@
     @Test
     public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
         String alias = "karlin";
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -171,7 +170,7 @@
 
     @Test
     public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(),
                 /*metadata=*/ null);
@@ -197,7 +196,7 @@
         return keyGenerator.generateKey();
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -207,6 +206,6 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
index 3ab3448..e605d75 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -106,7 +106,8 @@
                     RESULT_CODE);
 
             // Assert
-            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX);
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX);
         } finally {
             deleteSliently(dexMetadataPath);
             deleteSliently(apk);
@@ -135,7 +136,8 @@
                     RESULT_CODE);
 
             // Assert
-            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE);
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE);
         } finally {
             deleteSliently(dexMetadataPath);
             deleteSliently(apk);
@@ -164,7 +166,8 @@
                     RESULT_CODE);
 
             // Assert
-            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX);
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX);
         } finally {
             deleteSliently(dexMetadataPath);
             deleteSliently(apk);
@@ -191,7 +194,8 @@
                     RESULT_CODE);
 
             // Assert
-            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA);
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE);
         } finally {
             deleteSliently(apk);
         }
@@ -219,7 +223,8 @@
                     RESULT_CODE);
 
             // Assert
-            verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA);
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN);
         } finally {
             deleteSliently(dexMetadataPath);
             deleteSliently(apk);
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index 8874e0a..72e1e33 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
@@ -203,6 +204,28 @@
             }
         }
 
+        @Override
+        public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+            TranslationManagerServiceImpl service;
+            synchronized (mLock) {
+                service = getServiceForUserLocked(userId);
+            }
+            if (service != null) {
+                service.registerUiTranslationStateCallback(callback, Binder.getCallingUid());
+            }
+        }
+
+        @Override
+        public void unregisterUiTranslationStateCallback(IRemoteCallback callback, int userId) {
+            TranslationManagerServiceImpl service;
+            synchronized (mLock) {
+                service = getServiceForUserLocked(userId);
+            }
+            if (service != null) {
+                service.unregisterUiTranslationStateCallback(callback);
+            }
+        }
+
         /**
          * Dump the service state into the given stream. You run "adb shell dumpsys translation".
         */
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index ab6ac12..1ca07cb 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,17 +17,24 @@
 package com.android.server.translation;
 
 import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS;
+import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
+import static android.view.translation.UiTranslationManager.EXTRA_STATE;
+import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.service.translation.TranslationServiceInfo;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
+import android.view.inputmethod.InputMethodInfo;
 import android.view.translation.TranslationSpec;
 import android.view.translation.UiTranslationManager.UiTranslationState;
 
@@ -36,6 +43,7 @@
 import com.android.internal.util.SyncResultReceiver;
 import com.android.server.LocalServices;
 import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
 
@@ -174,5 +182,50 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Update UiTranslationState fail: " + e);
         }
+        invokeCallbacks(state, sourceSpec, destSpec);
     }
+
+    private void invokeCallbacks(
+            int state, TranslationSpec sourceSpec, TranslationSpec targetSpec) {
+        Bundle res = new Bundle();
+        res.putInt(EXTRA_STATE, state);
+        // TODO(177500482): Store the locale pair so it can be sent for RESUME events.
+        if (sourceSpec != null) {
+            res.putString(EXTRA_SOURCE_LOCALE, sourceSpec.getLanguage());
+            res.putString(EXTRA_TARGET_LOCALE, targetSpec.getLanguage());
+        }
+        // TODO(177500482): Only support the *current* Input Method.
+        List<InputMethodInfo> enabledInputMethods =
+                LocalServices.getService(InputMethodManagerInternal.class)
+                        .getEnabledInputMethodListAsUser(mUserId);
+        mCallbacks.broadcast((callback, uid) -> {
+            // Code here is non-optimal since it's temporary..
+            boolean isIme = false;
+            for (InputMethodInfo inputMethod : enabledInputMethods) {
+                if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) {
+                    isIme = true;
+                }
+            }
+            // TODO(177500482): Invoke it for the application being translated too.
+            if (!isIme) {
+                return;
+            }
+            try {
+                callback.sendResult(res);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e);
+            }
+        });
+    }
+
+    public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) {
+        mCallbacks.register(callback, sourceUid);
+        // TODO(177500482): trigger the callback here if we're already translating the UI.
+    }
+
+    public void unregisterUiTranslationStateCallback(IRemoteCallback callback) {
+        mCallbacks.unregister(callback);
+    }
+
+    private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
 }
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
index 5fb6b33..f5357b1 100644
--- a/telecomm/java/android/telecom/CallDiagnosticService.java
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -27,8 +27,6 @@
 import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.Annotation;
-import android.telephony.ims.ImsReasonInfo;
 import android.util.ArrayMap;
 
 import com.android.internal.telecom.ICallDiagnosticService;
@@ -59,7 +57,7 @@
  * </pre>
  * <p>
  * <h2>Threading Model</h2>
- * By default, all incoming IPC from Telecom in this service and in the {@link DiagnosticCall}
+ * By default, all incoming IPC from Telecom in this service and in the {@link CallDiagnostics}
  * instances will take place on the main thread.  You can override {@link #getExecutor()} in your
  * implementation to provide your own {@link Executor}.
  * @hide
@@ -116,26 +114,28 @@
     }
 
     /**
-     * Listens to events raised by a {@link DiagnosticCall}.
+     * Listens to events raised by a {@link CallDiagnostics}.
      */
-    private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener =
-            new android.telecom.DiagnosticCall.Listener() {
+    private CallDiagnostics.Listener mDiagnosticCallListener =
+            new CallDiagnostics.Listener() {
 
                 @Override
-                public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall,
-                        @DiagnosticCall.MessageType int message, int value) {
-                    handleSendDeviceToDeviceMessage(diagnosticCall, message, value);
+                public void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics,
+                        @CallDiagnostics.MessageType int message, int value) {
+                    handleSendDeviceToDeviceMessage(callDiagnostics, message, value);
                 }
 
                 @Override
-                public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+                public void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics,
+                        int messageId,
                         CharSequence message) {
-                    handleDisplayDiagnosticMessage(diagnosticCall, messageId, message);
+                    handleDisplayDiagnosticMessage(callDiagnostics, messageId, message);
                 }
 
                 @Override
-                public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
-                    handleClearDiagnosticMessage(diagnosticCall, messageId);
+                public void onClearDiagnosticMessage(CallDiagnostics callDiagnostics,
+                        int messageId) {
+                    handleClearDiagnosticMessage(callDiagnostics, messageId);
                 }
             };
 
@@ -149,7 +149,7 @@
      * Map which tracks the Telecom calls received from the Telecom stack.
      */
     private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>();
-    private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+    private final Map<String, CallDiagnostics> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
     private final Object mLock = new Object();
     private ICallDiagnosticServiceAdapter mAdapter;
 
@@ -177,7 +177,7 @@
      * executor you want to use for incoming IPC.
      *
      * @return the {@link Executor} to use for incoming IPC from Telecom to
-     * {@link CallDiagnosticService} and {@link DiagnosticCall}.
+     * {@link CallDiagnosticService} and {@link CallDiagnostics}.
      */
     @SuppressLint("OnNameExpected")
     @NonNull public Executor getExecutor() {
@@ -188,30 +188,30 @@
      * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call
      * which was added to Telecom.
      * <p>
-     * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be
+     * The {@link CallDiagnosticService} returns an implementation of {@link CallDiagnostics} to be
      * used for the lifespan of this call.
      * <p>
      * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
      * {@link CallDiagnosticService#getExecutor()} for more information.
      *
      * @param call The details of the new call.
-     * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
+     * @return An instance of {@link CallDiagnostics} which the {@link CallDiagnosticService}
      * provides to be used for the lifespan of the call.
-     * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned.
+     * @throws IllegalArgumentException if a {@code null} {@link CallDiagnostics} is returned.
      */
-    public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull
+    public abstract @NonNull CallDiagnostics onInitializeCallDiagnostics(@NonNull
             android.telecom.Call.Details call);
 
     /**
-     * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed.
-     * This happens when Telecom is no longer tracking the call in question.
+     * Telecom calls this method when a previous created {@link CallDiagnostics} is no longer
+     * needed.  This happens when Telecom is no longer tracking the call in question.
      * <p>
      * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see
      * {@link CallDiagnosticService#getExecutor()} for more information.
      *
      * @param call The diagnostic call which is no longer tracked by Telecom.
      */
-    public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
+    public abstract void onRemoveCallDiagnostics(@NonNull CallDiagnostics call);
 
     /**
      * Telecom calls this method when the audio routing or available audio route information
@@ -260,35 +260,35 @@
         }
 
         getExecutor().execute(() -> {
-            DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
-            if (diagnosticCall == null) {
+            CallDiagnostics callDiagnostics = onInitializeCallDiagnostics(newCallDetails);
+            if (callDiagnostics == null) {
                 throw new IllegalArgumentException(
                         "A valid DiagnosticCall instance was not provided.");
             }
             synchronized (mLock) {
-                diagnosticCall.setListener(mDiagnosticCallListener);
-                diagnosticCall.setCallId(telecomCallId);
-                mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+                callDiagnostics.setListener(mDiagnosticCallListener);
+                callDiagnostics.setCallId(telecomCallId);
+                mDiagnosticCallByTelecomCallId.put(telecomCallId, callDiagnostics);
             }
         });
     }
 
     /**
      * Handles an update to {@link Call.Details} notified by Telecom.
-     * Caches the call details and notifies the {@link DiagnosticCall} of the change via
-     * {@link DiagnosticCall#onCallDetailsChanged(Call.Details)}.
+     * Caches the call details and notifies the {@link CallDiagnostics} of the change via
+     * {@link CallDiagnostics#onCallDetailsChanged(Call.Details)}.
      * @param parcelableCall the new parceled call details from Telecom.
      */
     private void handleCallUpdated(@NonNull ParcelableCall parcelableCall) {
         String telecomCallId = parcelableCall.getId();
         Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId);
         Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
-        DiagnosticCall diagnosticCall;
+        CallDiagnostics callDiagnostics;
         synchronized (mLock) {
-            diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+            callDiagnostics = mDiagnosticCallByTelecomCallId.get(telecomCallId);
             mCallByTelecomCallId.put(telecomCallId, newCallDetails);
         }
-        getExecutor().execute(() -> diagnosticCall.handleCallUpdated(newCallDetails));
+        getExecutor().execute(() -> callDiagnostics.handleCallUpdated(newCallDetails));
     }
 
     /**
@@ -302,37 +302,37 @@
             mCallByTelecomCallId.remove(telecomCallId);
         }
 
-        DiagnosticCall diagnosticCall;
+        CallDiagnostics callDiagnostics;
         synchronized (mLock) {
             if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
-                diagnosticCall = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+                callDiagnostics = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
             } else {
-                diagnosticCall = null;
+                callDiagnostics = null;
             }
         }
 
         // Inform the service of the removed call.
-        if (diagnosticCall != null) {
-            getExecutor().execute(() -> onRemoveDiagnosticCall(diagnosticCall));
+        if (callDiagnostics != null) {
+            getExecutor().execute(() -> onRemoveCallDiagnostics(callDiagnostics));
         }
     }
 
     /**
      * Handles an incoming device to device message received from Telecom.  Notifies the
-     * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}.
+     * {@link CallDiagnostics} via {@link CallDiagnostics#onReceiveDeviceToDeviceMessage(int, int)}.
      * @param callId
      * @param message
      * @param value
      */
     private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) {
         Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value);
-        DiagnosticCall diagnosticCall;
+        CallDiagnostics callDiagnostics;
         synchronized (mLock) {
-            diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+            callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
         }
-        if (diagnosticCall != null) {
+        if (callDiagnostics != null) {
             getExecutor().execute(
-                    () -> diagnosticCall.onReceiveDeviceToDeviceMessage(message, value));
+                    () -> callDiagnostics.onReceiveDeviceToDeviceMessage(message, value));
         }
     }
 
@@ -345,12 +345,12 @@
     private void handleCallDisconnected(@NonNull String callId,
             @NonNull DisconnectCause disconnectCause) {
         Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause);
-        DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+        CallDiagnostics callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId);
         CharSequence message;
         if (disconnectCause.getImsReasonInfo() != null) {
-            message = diagnosticCall.onCallDisconnected(disconnectCause.getImsReasonInfo());
+            message = callDiagnostics.onCallDisconnected(disconnectCause.getImsReasonInfo());
         } else {
-            message = diagnosticCall.onCallDisconnected(
+            message = callDiagnostics.onCallDisconnected(
                     disconnectCause.getTelephonyDisconnectCause(),
                     disconnectCause.getTelephonyPreciseDisconnectCause());
         }
@@ -375,15 +375,15 @@
     }
 
     /**
-     * Handles a request from a {@link DiagnosticCall} to send a device to device message (received
-     * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}.
-     * @param diagnosticCall
+     * Handles a request from a {@link CallDiagnostics} to send a device to device message (received
+     * via {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}.
+     * @param callDiagnostics
      * @param message
      * @param value
      */
-    private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall,
+    private void handleSendDeviceToDeviceMessage(@NonNull CallDiagnostics callDiagnostics,
             int message, int value) {
-        String callId = diagnosticCall.getCallId();
+        String callId = callDiagnostics.getCallId();
         try {
             mAdapter.sendDeviceToDeviceMessage(callId, message, value);
             Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message,
@@ -395,15 +395,15 @@
     }
 
     /**
-     * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message.
-     * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}.
-     * @param diagnosticCall
+     * Handles a request from a {@link CallDiagnostics} to display an in-call diagnostic message.
+     * Originates from {@link CallDiagnostics#displayDiagnosticMessage(int, CharSequence)}.
+     * @param callDiagnostics
      * @param messageId
      * @param message
      */
-    private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+    private void handleDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId,
             CharSequence message) {
-        String callId = diagnosticCall.getCallId();
+        String callId = callDiagnostics.getCallId();
         try {
             mAdapter.displayDiagnosticMessage(callId, messageId, message);
             Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId,
@@ -415,14 +415,14 @@
     }
 
     /**
-     * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic
+     * Handles a request from a {@link CallDiagnostics} to clear a previously shown diagnostic
      * message.
-     * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}.
-     * @param diagnosticCall
+     * Originates from {@link CallDiagnostics#clearDiagnosticMessage(int)}.
+     * @param callDiagnostics
      * @param messageId
      */
-    private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
-        String callId = diagnosticCall.getCallId();
+    private void handleClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId) {
+        String callId = callDiagnostics.getCallId();
         try {
             mAdapter.clearDiagnosticMessage(callId, messageId);
             Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId);
diff --git a/telecomm/java/android/telecom/CallDiagnostics.java b/telecomm/java/android/telecom/CallDiagnostics.java
new file mode 100644
index 0000000..3356431
--- /dev/null
+++ b/telecomm/java/android/telecom/CallDiagnostics.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.telephony.Annotation;
+import android.telephony.CallQuality;
+import android.telephony.ims.ImsReasonInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link CallDiagnostics} provides a way for a {@link CallDiagnosticService} to receive diagnostic
+ * information about a mobile call on the device.  A {@link CallDiagnostics} instance is similar to
+ * a {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic
+ * and messaging capabilities not present on a {@link Call}.  The {@link CallDiagnosticService}
+ * creates a {@link CallDiagnostics} for each {@link Call} on the device.  This means that for each
+ * in progress call on the device, the {@link CallDiagnosticService} will create an instance of
+ * {@link CallDiagnostics}.
+ * <p>
+ * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the
+ * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable
+ * information about conditions impacting their call and corrective actions.  For example, if the
+ * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform
+ * the user that the call may soon drop and that they can try using a different calling method
+ * (e.g. VOIP or WIFI).
+ * <h2>Threading Model</h2>
+ * All incoming IPC from Telecom in this class will use the same {@link Executor} as the
+ * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more
+ * information.
+ * @hide
+ */
+@SystemApi
+public abstract class CallDiagnostics {
+
+    /**
+     * @hide
+     */
+    public interface Listener {
+        /**
+         * Used to inform the {@link CallDiagnosticService} of a request to send a D2d message
+         * @param callDiagnostics the call the message is from.
+         * @param message the message type
+         * @param value the message value
+         */
+        void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, int message, int value);
+
+        /**
+         * Used to inform the {@link CallDiagnosticService} of a request to display a diagnostic
+         * message.
+         * @param callDiagnostics the call the message pertains to.
+         * @param messageId an identifier for the message.
+         * @param message the message to display.
+         */
+        void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId,
+                CharSequence message);
+
+        /**
+         * Used to inform the {@link CallDiagnosticService} that a previously shown message is no
+         * longer pertinent.
+         * @param callDiagnostics the call the message pertains to.
+         * @param messageId the ID of the previously posted message.
+         */
+        void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId);
+    }
+
+    /**
+     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
+     * used for the current call.  The call network type communicated here is an intentional
+     * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which
+     * removes some of the resolution inherent in those values; the
+     * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is
+     * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for
+     * efficiency of transport.  For a discussion on the necessity of this simplification, see
+     * {@link #sendDeviceToDeviceMessage(int, int)}.
+     * <p>
+     * Valid values are below:
+     * <UL>
+     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI>
+     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI>
+     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI>
+     * </UL>
+     */
+    public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
+
+    /**
+     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
+     * used for the current call.
+     * <p>
+     * The audio codec communicated here is an intentional simplification of the
+     * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common
+     * variants of these audio codecs.  Other variants of these codecs are reported as the next
+     * closest variant.  For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec
+     * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}.
+     * For a discussion on the necessity of this simplification, see
+     * {@link #sendDeviceToDeviceMessage(int, int)}.
+     * <p>
+     * Valid values:
+     * <UL>
+     *     <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI>
+     *     <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI>
+     *     <LI>{@link Connection#AUDIO_CODEC_AMR}</LI>
+     * </UL>
+     */
+    public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
+
+    /**
+     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of
+     * the device.  Will typically mirror battery state reported via intents such as
+     * {@link android.content.Intent#ACTION_BATTERY_LOW}.
+     * <p>
+     * Valid values:
+     * <UL>
+     *     <LI>{@link #BATTERY_STATE_LOW}</LI>
+     *     <LI>{@link #BATTERY_STATE_GOOD}</LI>
+     *     <LI>{@link #BATTERY_STATE_CHARGING}</LI>
+     * </UL>
+     */
+    public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
+
+    /**
+     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network
+     * coverage as it pertains to the current call.  A {@link CallDiagnosticService} should signal
+     * poor coverage if the network coverage reaches a level where there is a high probability of
+     * the call dropping as a result.
+     * <p>
+     * Valid values:
+     * <UL>
+     *     <LI>{@link #COVERAGE_POOR}</LI>
+     *     <LI>{@link #COVERAGE_GOOD}</LI>
+     * </UL>
+     */
+    public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "MESSAGE_", value = {
+            MESSAGE_CALL_NETWORK_TYPE,
+            MESSAGE_CALL_AUDIO_CODEC,
+            MESSAGE_DEVICE_BATTERY_STATE,
+            MESSAGE_DEVICE_NETWORK_COVERAGE
+    })
+    public @interface MessageType {}
+
+    /**
+     * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
+     */
+    public static final int BATTERY_STATE_LOW = 1;
+
+    /**
+     * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low.
+     */
+    public static final int BATTERY_STATE_GOOD = 2;
+
+    /**
+     * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging.
+     */
+    public static final int BATTERY_STATE_CHARGING = 3;
+
+    /**
+     * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor.
+     */
+    public static final int COVERAGE_POOR = 1;
+
+    /**
+     * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good.
+     */
+    public static final int COVERAGE_GOOD = 2;
+
+    private Listener mListener;
+    private String mCallId;
+
+    /**
+     * @hide
+     */
+    public void setListener(@NonNull Listener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Sets the call ID for this {@link CallDiagnostics}.
+     * @param callId
+     * @hide
+     */
+    public void setCallId(@NonNull String callId) {
+        mCallId = callId;
+    }
+
+    /**
+     * @return the Telecom call ID for this {@link CallDiagnostics}.
+     * @hide
+     */
+    public @NonNull String getCallId() {
+        return mCallId;
+    }
+
+    /**
+     * Telecom calls this method when the details of a call changes.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
+     */
+    public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
+
+    /**
+     * The {@link CallDiagnosticService} implements this method to handle messages received via
+     * device to device communication.
+     * <p>
+     * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device
+     * communication.
+     * <p>
+     * The underlying device to device communication protocol assumes that where there the two
+     * devices communicating are using a different version of the protocol, messages the recipient
+     * are not aware of are silently discarded.  This means an older client talking to a new client
+     * will not receive newer messages and values sent by the new client.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
+     */
+    public abstract void onReceiveDeviceToDeviceMessage(
+            @MessageType int message,
+            int value);
+
+    /**
+     * Sends a device to device message to the device on the other end of this call.
+     * <p>
+     * Device to device communication is an Android platform feature which supports low bandwidth
+     * communication between Android devices while they are in a call.  The device to device
+     * communication leverages DTMF tones or RTP header extensions to pass messages.  The
+     * messages are unacknowledged and sent in a best-effort manner.  The protocols assume that the
+     * nature of the message are informational only and are used only to convey basic state
+     * information between devices.
+     * <p>
+     * Device to device messages are intentional simplifications of more rich indicators in the
+     * platform due to the extreme bandwidth constraints inherent with underlying device to device
+     * communication transports used by the telephony framework.  Device to device communication is
+     * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
+     * for a call, or using the DTMF digits A-D as a communication pathway.  RTP header extension
+     * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for
+     * a message.  Signalling requirements for DTMF digits place even more significant limitations
+     * on the amount of information which can be communicated during a call, offering only a few
+     * bits of potential information per message.  The messages and values are constrained in order
+     * to meet the limited bandwidth inherent with DTMF signalling.
+     * <p>
+     * Allowed message types are:
+     * <ul>
+     *     <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI>
+     *     <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI>
+     *     <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI>
+     *     <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI>
+     * </ul>
+     * @param message The message type to send.
+     * @param value The message value corresponding to the type.
+     */
+    public final void sendDeviceToDeviceMessage(int message, int value) {
+        if (mListener != null) {
+            mListener.onSendDeviceToDeviceMessage(this, message, value);
+        }
+    }
+
+    /**
+     * Telecom calls this method when a GSM or CDMA call disconnects.
+     * The CallDiagnosticService can return a human readable disconnect message which will be passed
+     * to the Dialer app as the {@link DisconnectCause#getDescription()}.  A dialer app typically
+     * shows this message at the termination of the call.  If {@code null} is returned, the
+     * disconnect message generated by the telephony stack will be shown instead.
+     * <p>
+     * @param disconnectCause the disconnect cause for the call.
+     * @param preciseDisconnectCause the precise disconnect cause for the call.
+     * @return the disconnect message to use in place of the default Telephony message, or
+     * {@code null} if the default message will not be overridden.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
+     */
+    // TODO: Wire in Telephony support for this.
+    public abstract @Nullable CharSequence onCallDisconnected(
+            @Annotation.DisconnectCauses int disconnectCause,
+            @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+
+    /**
+     * Telecom calls this method when an IMS call disconnects and Telephony has already
+     * provided the disconnect reason info and disconnect message for the call.  The
+     * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and
+     * combine it with other call diagnostic information it is aware of to override the disconnect
+     * call message if desired.
+     *
+     * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
+     * @return A user-readable call disconnect message to use in place of the platform-generated
+     * disconnect message, or {@code null} if the disconnect message should not be overridden.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
+     */
+    // TODO: Wire in Telephony support for this.
+    public abstract @Nullable CharSequence onCallDisconnected(
+            @NonNull ImsReasonInfo disconnectReason);
+
+    /**
+     * Telecom calls this method when a {@link CallQuality} report is received from the telephony
+     * stack for a call.
+     * @param callQuality The call quality report for this call.
+     * <p>
+     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
+     * see {@link CallDiagnosticService#getExecutor()} for more information.
+     */
+    public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
+
+     /**
+      * Signals the active default dialer app to display a call diagnostic message.  This can be
+      * used to report problems encountered during the span of a call.
+      * <p>
+      * The {@link CallDiagnosticService} provides a unique client-specific identifier used to
+      * identify the specific diagnostic message type.
+      * <p>
+      * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the
+      * diagnostic condition has cleared.
+      * @param messageId the unique message identifier.
+      * @param message a human-readable, localized message to be shown to the user indicating a
+      *                call issue which has occurred, along with potential mitigating actions.
+     */
+    public final void displayDiagnosticMessage(int messageId, @NonNull
+            CharSequence message) {
+        if (mListener != null) {
+            mListener.onDisplayDiagnosticMessage(this, messageId, message);
+        }
+    }
+
+    /**
+     * Signals to the active default dialer that the diagnostic message previously signalled using
+     * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no
+     * longer applicable (e.g. service has improved, for example.
+     * @param messageId the message identifier for a message previously shown via
+     *                  {@link #displayDiagnosticMessage(int, CharSequence)}.
+     */
+    public final void clearDiagnosticMessage(int messageId) {
+        if (mListener != null) {
+            mListener.onClearDiagnosticMessage(this, messageId);
+        }
+    }
+
+    /**
+     * Called by the {@link CallDiagnosticService} to update the call details for this
+     * {@link CallDiagnostics} based on an update received from Telecom.
+     * @param newDetails the new call details.
+     * @hide
+     */
+    public void handleCallUpdated(@NonNull Call.Details newDetails) {
+        onCallDetailsChanged(newDetails);
+    }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 335857af8..6dab6df 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -947,7 +947,7 @@
      * {@link CallDiagnosticService} implementation which is active.
      * <p>
      * Likewise, if a {@link CallDiagnosticService} sends a message using
-     * {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony
+     * {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony
      * via {@link Connection#onCallEvent(String, Bundle)}.  The telephony stack will relay the
      * message to the other device.
      * @hide
@@ -960,7 +960,7 @@
      * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
      * message type.
      *
-     * See {@link DiagnosticCall} for more information.
+     * See {@link CallDiagnostics} for more information.
      * @hide
      */
     @SystemApi
@@ -971,7 +971,7 @@
      * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
      * message value.
      * <p>
-     * See {@link DiagnosticCall} for more information.
+     * See {@link CallDiagnostics} for more information.
      * @hide
      */
     @SystemApi
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java
index af46b77..a6b7258 100644
--- a/telecomm/java/android/telecom/DiagnosticCall.java
+++ b/telecomm/java/android/telecom/DiagnosticCall.java
@@ -16,338 +16,12 @@
 
 package android.telecom;
 
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.telephony.Annotation;
-import android.telephony.CallQuality;
-import android.telephony.ims.ImsReasonInfo;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.Executor;
 
 /**
- * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic
- * information about a mobile call on the device.  A {@link DiagnosticCall} is similar to a
- * {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic
- * and messaging capabilities not present on a {@link Call}.  The {@link CallDiagnosticService}
- * creates a {@link DiagnosticCall} for each {@link Call} on the device.  This means that for each
- * in progress call on the device, the {@link CallDiagnosticService} will create an instance of
- * {@link DiagnosticCall}.
- * <p>
- * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the
- * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable
- * information about conditions impacting their call and corrective actions.  For example, if the
- * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform
- * the user that the call may soon drop and that they can try using a different calling method
- * (e.g. VOIP or WIFI).
- * <h2>Threading Model</h2>
- * All incoming IPC from Telecom in this class will use the same {@link Executor} as the
- * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more
- * information.
+ * @deprecated use {@link CallDiagnostics} instead.
  * @hide
  */
 @SystemApi
-public abstract class DiagnosticCall {
-
-    /**
-     * @hide
-     */
-    public interface Listener {
-        void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value);
-        void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
-                CharSequence message);
-        void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId);
-    }
-
-    /**
-     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
-     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
-     * used for the current call.  The call network type communicated here is an intentional
-     * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which
-     * removes some of the resolution inherent in those values; the
-     * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is
-     * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for
-     * efficiency of transport.  For a discussion on the necessity of this simplification, see
-     * {@link #sendDeviceToDeviceMessage(int, int)}.
-     * <p>
-     * Valid values are below:
-     * <UL>
-     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI>
-     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI>
-     *     <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI>
-     * </UL>
-     */
-    public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
-
-    /**
-     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
-     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
-     * used for the current call.
-     * <p>
-     * The audio codec communicated here is an intentional simplification of the
-     * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common
-     * variants of these audio codecs.  Other variants of these codecs are reported as the next
-     * closest variant.  For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec
-     * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}.
-     * For a discussion on the necessity of this simplification, see
-     * {@link #sendDeviceToDeviceMessage(int, int)}.
-     * <p>
-     * Valid values:
-     * <UL>
-     *     <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI>
-     *     <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI>
-     *     <LI>{@link Connection#AUDIO_CODEC_AMR}</LI>
-     * </UL>
-     */
-    public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
-
-    /**
-     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
-     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of
-     * the device.  Will typically mirror battery state reported via intents such as
-     * {@link android.content.Intent#ACTION_BATTERY_LOW}.
-     * <p>
-     * Valid values:
-     * <UL>
-     *     <LI>{@link #BATTERY_STATE_LOW}</LI>
-     *     <LI>{@link #BATTERY_STATE_GOOD}</LI>
-     *     <LI>{@link #BATTERY_STATE_CHARGING}</LI>
-     * </UL>
-     */
-    public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
-
-    /**
-     * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
-     * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network
-     * coverage as it pertains to the current call.  A {@link CallDiagnosticService} should signal
-     * poor coverage if the network coverage reaches a level where there is a high probability of
-     * the call dropping as a result.
-     * <p>
-     * Valid values:
-     * <UL>
-     *     <LI>{@link #COVERAGE_POOR}</LI>
-     *     <LI>{@link #COVERAGE_GOOD}</LI>
-     * </UL>
-     */
-    public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
-
-    /**@hide*/
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "MESSAGE_", value = {
-            MESSAGE_CALL_NETWORK_TYPE,
-            MESSAGE_CALL_AUDIO_CODEC,
-            MESSAGE_DEVICE_BATTERY_STATE,
-            MESSAGE_DEVICE_NETWORK_COVERAGE
-    })
-    public @interface MessageType {}
-
-    /**
-     * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
-     */
-    public static final int BATTERY_STATE_LOW = 1;
-
-    /**
-     * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low.
-     */
-    public static final int BATTERY_STATE_GOOD = 2;
-
-    /**
-     * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging.
-     */
-    public static final int BATTERY_STATE_CHARGING = 3;
-
-    /**
-     * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor.
-     */
-    public static final int COVERAGE_POOR = 1;
-
-    /**
-     * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good.
-     */
-    public static final int COVERAGE_GOOD = 2;
-
-    private Listener mListener;
-    private String mCallId;
-
-    /**
-     * @hide
-     */
-    public void setListener(@NonNull Listener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Sets the call ID for this {@link DiagnosticCall}.
-     * @param callId
-     * @hide
-     */
-    public void setCallId(@NonNull String callId) {
-        mCallId = callId;
-    }
-
-    /**
-     * @return the Telecom call ID for this {@link DiagnosticCall}.
-     * @hide
-     */
-    public @NonNull String getCallId() {
-        return mCallId;
-    }
-
-    /**
-     * Telecom calls this method when the details of a call changes.
-     * <p>
-     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
-     * see {@link CallDiagnosticService#getExecutor()} for more information.
-     */
-    public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
-
-    /**
-     * The {@link CallDiagnosticService} implements this method to handle messages received via
-     * device to device communication.
-     * <p>
-     * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device
-     * communication.
-     * <p>
-     * The underlying device to device communication protocol assumes that where there the two
-     * devices communicating are using a different version of the protocol, messages the recipient
-     * are not aware of are silently discarded.  This means an older client talking to a new client
-     * will not receive newer messages and values sent by the new client.
-     * <p>
-     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
-     * see {@link CallDiagnosticService#getExecutor()} for more information.
-     */
-    public abstract void onReceiveDeviceToDeviceMessage(
-            @MessageType int message,
-            int value);
-
-    /**
-     * Sends a device to device message to the device on the other end of this call.
-     * <p>
-     * Device to device communication is an Android platform feature which supports low bandwidth
-     * communication between Android devices while they are in a call.  The device to device
-     * communication leverages DTMF tones or RTP header extensions to pass messages.  The
-     * messages are unacknowledged and sent in a best-effort manner.  The protocols assume that the
-     * nature of the message are informational only and are used only to convey basic state
-     * information between devices.
-     * <p>
-     * Device to device messages are intentional simplifications of more rich indicators in the
-     * platform due to the extreme bandwidth constraints inherent with underlying device to device
-     * communication transports used by the telephony framework.  Device to device communication is
-     * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
-     * for a call, or using the DTMF digits A-D as a communication pathway.  RTP header extension
-     * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for
-     * a message.  Signalling requirements for DTMF digits place even more significant limitations
-     * on the amount of information which can be communicated during a call, offering only a few
-     * bits of potential information per message.  The messages and values are constrained in order
-     * to meet the limited bandwidth inherent with DTMF signalling.
-     * <p>
-     * Allowed message types are:
-     * <ul>
-     *     <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI>
-     *     <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI>
-     *     <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI>
-     *     <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI>
-     * </ul>
-     * @param message The message type to send.
-     * @param value The message value corresponding to the type.
-     */
-    public final void sendDeviceToDeviceMessage(int message, int value) {
-        if (mListener != null) {
-            mListener.onSendDeviceToDeviceMessage(this, message, value);
-        }
-    }
-
-    /**
-     * Telecom calls this method when a GSM or CDMA call disconnects.
-     * The CallDiagnosticService can return a human readable disconnect message which will be passed
-     * to the Dialer app as the {@link DisconnectCause#getDescription()}.  A dialer app typically
-     * shows this message at the termination of the call.  If {@code null} is returned, the
-     * disconnect message generated by the telephony stack will be shown instead.
-     * <p>
-     * @param disconnectCause the disconnect cause for the call.
-     * @param preciseDisconnectCause the precise disconnect cause for the call.
-     * @return the disconnect message to use in place of the default Telephony message, or
-     * {@code null} if the default message will not be overridden.
-     * <p>
-     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
-     * see {@link CallDiagnosticService#getExecutor()} for more information.
-     */
-    // TODO: Wire in Telephony support for this.
-    public abstract @Nullable CharSequence onCallDisconnected(
-            @Annotation.DisconnectCauses int disconnectCause,
-            @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
-
-    /**
-     * Telecom calls this method when an IMS call disconnects and Telephony has already
-     * provided the disconnect reason info and disconnect message for the call.  The
-     * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and
-     * combine it with other call diagnostic information it is aware of to override the disconnect
-     * call message if desired.
-     *
-     * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
-     * @return A user-readable call disconnect message to use in place of the platform-generated
-     * disconnect message, or {@code null} if the disconnect message should not be overridden.
-     * <p>
-     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
-     * see {@link CallDiagnosticService#getExecutor()} for more information.
-     */
-    // TODO: Wire in Telephony support for this.
-    public abstract @Nullable CharSequence onCallDisconnected(
-            @NonNull ImsReasonInfo disconnectReason);
-
-    /**
-     * Telecom calls this method when a {@link CallQuality} report is received from the telephony
-     * stack for a call.
-     * @param callQuality The call quality report for this call.
-     * <p>
-     * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService};
-     * see {@link CallDiagnosticService#getExecutor()} for more information.
-     */
-    public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
-
-     /**
-      * Signals the active default dialer app to display a call diagnostic message.  This can be
-      * used to report problems encountered during the span of a call.
-      * <p>
-      * The {@link CallDiagnosticService} provides a unique client-specific identifier used to
-      * identify the specific diagnostic message type.
-      * <p>
-      * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the
-      * diagnostic condition has cleared.
-      * @param messageId the unique message identifier.
-      * @param message a human-readable, localized message to be shown to the user indicating a
-      *                call issue which has occurred, along with potential mitigating actions.
-     */
-    public final void displayDiagnosticMessage(int messageId, @NonNull
-            CharSequence message) {
-        if (mListener != null) {
-            mListener.onDisplayDiagnosticMessage(this, messageId, message);
-        }
-    }
-
-    /**
-     * Signals to the active default dialer that the diagnostic message previously signalled using
-     * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no
-     * longer applicable (e.g. service has improved, for example.
-     * @param messageId the message identifier for a message previously shown via
-     *                  {@link #displayDiagnosticMessage(int, CharSequence)}.
-     */
-    public final void clearDiagnosticMessage(int messageId) {
-        if (mListener != null) {
-            mListener.onClearDiagnosticMessage(this, messageId);
-        }
-    }
-
-    /**
-     * Called by the {@link CallDiagnosticService} to update the call details for this
-     * {@link DiagnosticCall} based on an update received from Telecom.
-     * @param newDetails the new call details.
-     * @hide
-     */
-    public void handleCallUpdated(@NonNull Call.Details newDetails) {
-        onCallDetailsChanged(newDetails);
-    }
+public abstract class DiagnosticCall extends CallDiagnostics {
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a5bbd08..74421a0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2974,6 +2974,18 @@
     public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
 
     /**
+     * Indicates if the carrier supports upgrading a call that was previously an RTT call to VT.
+     */
+    public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL =
+            "vt_upgrade_supported_for_downgraded_rtt_call";
+
+    /**
+     * Indicates if the carrier supports upgrading a call that was previously a VT call to RTT.
+     */
+    public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL =
+            "rtt_upgrade_supported_for_downgraded_vt_call";
+
+    /**
      * Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
      */
     public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
@@ -5172,6 +5184,8 @@
         sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index faa5deed..962200b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15454,7 +15454,9 @@
     public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
 
     /**
-     * The unattended reboot was not prepared due to generic error.
+     * The unattended reboot was not prepared due to a non-recoverable error. After this error,
+     * the client that manages the unattended reboot should not try to invoke the API again
+     * until the next power cycle.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b30dd26..96af172 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -54,6 +54,7 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -2374,6 +2375,41 @@
      void setDeviceUceEnabled(boolean isEnabled);
 
     /**
+     * Add feature tags to the IMS registration being tracked by UCE and potentially
+     * generate a new PUBLISH to the network.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability addUceRegistrationOverrideShell(int subId, in List<String> featureTags);
+
+    /**
+     * Remove feature tags from the IMS registration being tracked by UCE and potentially
+     * generate a new PUBLISH to the network.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+            in List<String> featureTags);
+
+    /**
+     * Clear overridden feature tags in the IMS registration being tracked by UCE and potentially
+     * generate a new PUBLISH to the network.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability clearUceRegistrationOverrideShell(int subId);
+
+    /**
+     * Get the latest RcsContactUceCapability structure that is used in SIP PUBLISH procedures.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId);
+
+    /**
+     * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+     * device does not have an active PUBLISH.
+     * Note: This is designed for a SHELL command only.
+     */
+    String getLastUcePidfXmlShell(int subId);
+
+    /**
      * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
      * specified thresholds.
      */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 3c12aaa..c92d40c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.close
 
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -50,12 +49,7 @@
         @JvmStatic
         fun getParams(): List<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(
-                    repetitions = 5,
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
-                )
+                .getConfigNonRotationTests(repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 8359ccf..1f880f6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.close
 
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -50,12 +49,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(
-                    repetitions = 5,
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
-                )
+                .getConfigNonRotationTests(repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 417a5c5..a9888b1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -62,11 +61,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
-                )
+                .getConfigNonRotationTests()
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 5d015d9..2e59dac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.test.annotations.Presubmit
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -135,12 +134,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(
-                    repetitions = 5,
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
-                )
+                .getConfigNonRotationTests(repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index bdae810..dee7e59 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -74,11 +73,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
-                )
+                .getConfigNonRotationTests()
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 80b2237..6985b36 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.platform.test.annotations.Presubmit
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -71,12 +70,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigRotationTests(
-                    repetitions = 5,
-                    supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                    )
-                )
+                .getConfigRotationTests(repetitions = 5)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 24417f4..e914f64 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -28,12 +28,10 @@
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
@@ -48,12 +46,7 @@
     protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
 
     protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
-        withTestName { testSpec.name }
-        repeat { testSpec.config.repetitions }
         setup {
-            test {
-                device.wakeUpAndGoToHomeScreen()
-            }
             eachRun {
                 this.setRotation(testSpec.config.startRotation)
             }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index dd7103c..45d3006 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.rotation
 
 import android.platform.test.annotations.Presubmit
-import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -104,12 +103,7 @@
 
         @JvmStatic
         private fun getConfigurations(): List<FlickerTestParameter> {
-            return testFactory.getConfigRotationTests(
-                repetitions = 2,
-                supportedNavigationModes = listOf(
-                    WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                )
-            ).flatMap {
+            return testFactory.getConfigRotationTests(repetitions = 2).flatMap {
                 val defaultRun = it.createConfig(starveUiThread = false)
                 val busyUiRun = it.createConfig(starveUiThread = true)
                 listOf(
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fadd1ea..a0b13c8 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,8 @@
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -35,11 +37,14 @@
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
 import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
@@ -183,7 +188,7 @@
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
 import android.net.IQosCallback;
 import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
@@ -202,6 +207,7 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
@@ -377,6 +383,11 @@
     // Set a non-zero value to verify the flow to set tcp init rwnd value.
     private static final int TEST_TCP_INIT_RWND = 60;
 
+    // Used for testing the per-work-profile default network.
+    private static final int TEST_APP_ID = 103;
+    private static final int TEST_WORK_PROFILE_USER_ID = 2;
+    private static final int TEST_WORK_PROFILE_APP_UID =
+            UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
@@ -420,6 +431,7 @@
     private VpnManagerService mVpnManagerService;
     private TestNetworkCallback mDefaultNetworkCallback;
     private TestNetworkCallback mSystemDefaultNetworkCallback;
+    private TestNetworkCallback mProfileDefaultNetworkCallback;
 
     // State variables required to emulate NetworkPolicyManagerService behaviour.
     private int mUidRules = RULE_NONE;
@@ -541,13 +553,26 @@
             return super.getSystemService(name);
         }
 
+        final HashMap<UserHandle, UserManager> mUserManagers = new HashMap<>();
         @Override
         public Context createContextAsUser(UserHandle user, int flags) {
             final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this));
             doReturn(user).when(asUser).getUser();
+            doAnswer((inv) -> {
+                final UserManager um = mUserManagers.computeIfAbsent(user,
+                        u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager)));
+                return um;
+            }).when(asUser).getSystemService(Context.USER_SERVICE);
             return asUser;
         }
 
+        public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) {
+            // This relies on all contexts for a given user returning the same UM mock
+            final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
+                    .getSystemService(UserManager.class);
+            doReturn(value).when(umMock).isManagedProfile();
+        }
+
         @Override
         public ContentResolver getContentResolver() {
             return mContentResolver;
@@ -1078,6 +1103,10 @@
         public void triggerUnfulfillable(NetworkRequest r) {
             super.releaseRequestAsUnfulfillableByAnyFactory(r);
         }
+
+        public void assertNoRequestChanged() {
+            assertNull(mRequestHistory.poll(0, r -> true));
+        }
     }
 
     private Set<UidRange> uidRangesForUids(int... uids) {
@@ -1403,17 +1432,36 @@
         fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
     }
 
-    private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
-            int uid) {
+    private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
         when(mDeps.getCallingUid()).thenReturn(uid);
         try {
-            mCm.registerNetworkCallback(request, callback);
-            waitForIdle();
+            return what.get();
         } finally {
             returnRealCallingUid();
         }
     }
 
+    private void doAsUid(final int uid, @NonNull final Runnable what) {
+        doAsUid(uid, () -> {
+            what.run(); return Void.TYPE;
+        });
+    }
+
+    private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
+            int uid) {
+        doAsUid(uid, () -> {
+            mCm.registerNetworkCallback(request, callback);
+        });
+    }
+
+    private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback,
+            final int uid) {
+        doAsUid(uid, () -> {
+            mCm.registerDefaultNetworkCallback(callback);
+            waitForIdle();
+        });
+    }
+
     private static final int PRIMARY_USER = 0;
     private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
     private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
@@ -1460,6 +1508,9 @@
             Looper.prepare();
         }
         mockDefaultPackages();
+        mockHasSystemFeature(FEATURE_WIFI, true);
+        mockHasSystemFeature(FEATURE_WIFI_DIRECT, true);
+        doReturn(true).when(mTelephonyManager).isDataCapable();
 
         FakeSettingsProvider.clearSettingsProvider();
         mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
@@ -1512,10 +1563,7 @@
     }
 
     private ConnectivityService.Dependencies makeDependencies() {
-        doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
-                .getInt("net.tcp.default_init_rwnd", 0);
         doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
-        doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
         final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
         doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
         doReturn(mNetIdManager).when(deps).makeNetIdManager();
@@ -1573,6 +1621,7 @@
     @After
     public void tearDown() throws Exception {
         unregisterDefaultNetworkCallbacks();
+        maybeTearDownEnterpriseNetwork();
         setAlwaysOnNetworks(false);
         if (mCellNetworkAgent != null) {
             mCellNetworkAgent.disconnect();
@@ -1783,7 +1832,8 @@
         assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
         assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
         assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
-        assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+        assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+        assertFalse(mCm.isNetworkSupported(TYPE_PROXY));
 
         // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
         // mocks, this assert exercises the ConnectivityService code path that ensures that
@@ -7992,7 +8042,6 @@
 
         // Switching default network updates TCP buffer sizes.
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
-        verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
         // the NAT64 prefix was removed because one was never discovered.
         cellLp.addLinkAddress(myIpv4);
@@ -8478,14 +8527,12 @@
         mCellNetworkAgent.connect(false);
         networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
-        verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Change link Properties should have updated tcp buffer size.
         LinkProperties lp = new LinkProperties();
         lp.setTcpBufferSizes(testTcpBufferSizes);
         mCellNetworkAgent.sendLinkProperties(lp);
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verifyTcpBufferSizeChange(testTcpBufferSizes);
-        verify(mSystemProperties, times(2)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Clean up.
         mCellNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -9160,7 +9207,8 @@
                 ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
                 TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
         return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
-                nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
+                nc, new NetworkScore.Builder().setLegacyInt(0).build(),
+                mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
                 INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies());
     }
 
@@ -10112,9 +10160,12 @@
                 Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
         mSystemDefaultNetworkCallback = new TestNetworkCallback();
         mDefaultNetworkCallback = new TestNetworkCallback();
+        mProfileDefaultNetworkCallback = new TestNetworkCallback();
         mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
                 new Handler(ConnectivityThread.getInstanceLooper()));
         mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
+        registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
+                TEST_WORK_PROFILE_APP_UID);
         mServiceContext.setPermission(
                 Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
     }
@@ -10126,6 +10177,9 @@
         if (null != mSystemDefaultNetworkCallback) {
             mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
         }
+        if (null != mProfileDefaultNetworkCallback) {
+            mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
+        }
     }
 
     private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -10181,7 +10235,7 @@
         oemPrefListener.expectOnComplete();
     }
 
-    private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
+    private static class TestOemListenerCallback implements IOnCompleteListener {
         final CompletableFuture<Object> mDone = new CompletableFuture<>();
 
         @Override
@@ -11119,11 +11173,495 @@
         mCm.unregisterNetworkCallback(cellCb);
     }
 
+    // Cannot be part of MockNetworkFactory since it requires method of the test.
+    private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) {
+        waitForIdle();
+        factory.assertNoRequestChanged();
+    }
+
     @Test
-    public void testRegisterBestMatchingNetworkCallback() throws Exception {
-        final NetworkRequest request = new NetworkRequest.Builder().build();
-        assertThrows(UnsupportedOperationException.class,
-                () -> mCm.registerBestMatchingNetworkCallback(request, new NetworkCallback(),
-                        mCsHandlerThread.getThreadHandler()));
+    public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception {
+        // Prepare mock mms factory.
+        final HandlerThread handlerThread = new HandlerThread("MockCellularFactory");
+        handlerThread.start();
+        NetworkCapabilities filter = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_MMS);
+        final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "testFactory", filter, mCsHandlerThread);
+        testFactory.setScoreFilter(40);
+
+        try {
+            // Register the factory and expect it will see default request, because all requests
+            // are sent to all factories.
+            testFactory.register();
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(1);
+            // The factory won't try to start the network since the default request doesn't
+            // match the filter (no INTERNET capability).
+            assertFalse(testFactory.getMyStartRequested());
+
+            // Register callback for listening best matching network. Verify that the request won't
+            // be sent to factory.
+            final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+            mCm.registerBestMatchingNetworkCallback(
+                    new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+                    bestMatchingCb, mCsHandlerThread.getThreadHandler());
+            bestMatchingCb.assertNoCallback();
+            expectNoRequestChanged(testFactory);
+            testFactory.assertRequestCountEquals(1);
+            assertFalse(testFactory.getMyStartRequested());
+
+            // Fire a normal mms request, verify the factory will only see the request.
+            final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback();
+            final NetworkRequest mmsRequest = new NetworkRequest.Builder()
+                    .addCapability(NET_CAPABILITY_MMS).build();
+            mCm.requestNetwork(mmsRequest, mmsNetworkCallback);
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(2);
+            assertTrue(testFactory.getMyStartRequested());
+
+            // Unregister best matching callback, verify factory see no change.
+            mCm.unregisterNetworkCallback(bestMatchingCb);
+            expectNoRequestChanged(testFactory);
+            testFactory.assertRequestCountEquals(2);
+            assertTrue(testFactory.getMyStartRequested());
+        } finally {
+            testFactory.terminate();
+        }
+    }
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception {
+        final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+        mCm.registerBestMatchingNetworkCallback(
+                new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(),
+                bestMatchingCb, mCsHandlerThread.getThreadHandler());
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Change something on cellular to trigger capabilities changed, since the callback
+        // only cares about the best network, verify it received nothing from cellular.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        bestMatchingCb.assertNoCallback();
+
+        // Make cellular the best network again, verify the callback now tracks cellular.
+        mWiFiNetworkAgent.adjustScore(-50);
+        bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+        // Make cellular temporary non-trusted, which will not satisfying the request.
+        // Verify the callback switch from/to the other network accordingly.
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+        bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED);
+        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent);
+
+        // Verify the callback doesn't care about wifi disconnect.
+        mWiFiNetworkAgent.disconnect();
+        bestMatchingCb.assertNoCallback();
+        mCellNetworkAgent.disconnect();
+        bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+    }
+
+    private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
+        UidRange range = UidRange.createForUser(handle);
+        return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) };
+    }
+
+    private static class TestOnCompleteListener implements Runnable {
+        final class OnComplete {}
+        final ArrayTrackRecord<OnComplete>.ReadHead mHistory =
+                new ArrayTrackRecord<OnComplete>().newReadHead();
+
+        @Override
+        public void run() {
+            mHistory.add(new OnComplete());
+        }
+
+        public void expectOnComplete() {
+            assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true));
+        }
+    }
+
+    private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception {
+        final NetworkCapabilities workNc = new NetworkCapabilities();
+        workNc.addCapability(NET_CAPABILITY_ENTERPRISE);
+        workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+        return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
+    }
+
+    private TestNetworkCallback mEnterpriseCallback;
+    private UserHandle setupEnterpriseNetwork() {
+        final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(userHandle, true);
+
+        // File a request to avoid the enterprise network being disconnected as soon as the default
+        // request goes away – it would make impossible to test that networkRemoveUidRanges
+        // is called, as the network would disconnect first for lack of a request.
+        mEnterpriseCallback = new TestNetworkCallback();
+        final NetworkRequest keepUpRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_ENTERPRISE)
+                .build();
+        mCm.requestNetwork(keepUpRequest, mEnterpriseCallback);
+        return userHandle;
+    }
+
+    private void maybeTearDownEnterpriseNetwork() {
+        if (null != mEnterpriseCallback) {
+            mCm.unregisterNetworkCallback(mEnterpriseCallback);
+        }
+    }
+
+    /**
+     * Make sure per-profile networking preference behaves as expected when the enterprise network
+     * goes up and down while the preference is active. Make sure they behave as expected whether
+     * there is a general default network or not.
+     */
+    @Test
+    public void testPreferenceForUserNetworkUpDown() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle = setupEnterpriseNetwork();
+        registerDefaultNetworkCallbacks();
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+
+        // Setting a network preference for this user will create a new set of routing rules for
+        // the UID range that corresponds to this user, so as to define the default network
+        // for these apps separately. This is true because the multi-layer request relevant to
+        // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific
+        // rules to the correct network – in this case the system default network. The case where
+        // the default network for the profile happens to be the same as the system default
+        // is not handled specially, the rules are always active as long as a preference is set.
+        inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        // The enterprise network is not ready yet.
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                mProfileDefaultNetworkCallback);
+
+        final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+        workAgent.connect(false);
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
+        mSystemDefaultNetworkCallback.assertNoCallback();
+        mDefaultNetworkCallback.assertNoCallback();
+        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        // Make sure changes to the work agent send callbacks to the app in the work profile, but
+        // not to the other apps.
+        workAgent.setNetworkValid(true /* isStrictMode */);
+        workAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
+                nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)
+                        && nc.hasCapability(NET_CAPABILITY_ENTERPRISE));
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+        workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+        // Conversely, change a capability on the system-wide default network and make sure
+        // that only the apps outside of the work profile receive the callbacks.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        mProfileDefaultNetworkCallback.assertNoCallback();
+
+        // Disconnect and reconnect the system-wide default network and make sure that the
+        // apps on this network see the appropriate callbacks, and the app on the work profile
+        // doesn't because it continues to use the enterprise network.
+        mCellNetworkAgent.disconnect();
+        mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.assertNoCallback();
+        waitForIdle();
+        inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.assertNoCallback();
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+
+        // When the agent disconnects, test that the app on the work profile falls back to the
+        // default network.
+        workAgent.disconnect();
+        mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+        waitForIdle();
+        inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
+
+        mCellNetworkAgent.disconnect();
+        mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+
+        waitForIdle();
+        inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+        // If the control comes here, callbacks seem to behave correctly in the presence of
+        // a default network when the enterprise network goes up and down. Now, make sure they
+        // also behave correctly in the absence of a system-wide default network.
+        final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
+        workAgent2.connect(false);
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
+                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        workAgent2.setNetworkValid(true /* isStrictMode */);
+        workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid());
+        mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
+                nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
+                        && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+
+        // When the agent disconnects, test that the app on the work profile falls back to the
+        // default network.
+        workAgent2.disconnect();
+        mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        waitForIdle();
+        inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId);
+
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                mProfileDefaultNetworkCallback);
+
+        // Callbacks will be unregistered by tearDown()
+    }
+
+    /**
+     * Test that, in a given networking context, calling setPreferenceForUser to set per-profile
+     * defaults on then off works as expected.
+     */
+    @Test
+    public void testSetPreferenceForUserOnOff() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle = setupEnterpriseNetwork();
+
+        // Connect both a regular cell agent and an enterprise network first.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+        workAgent.connect(true);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        registerDefaultNetworkCallbacks();
+
+        mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        workAgent.disconnect();
+        mCellNetworkAgent.disconnect();
+
+        // Callbacks will be unregistered by tearDown()
+    }
+
+    /**
+     * Test per-profile default networks for two different profiles concurrently.
+     */
+    @Test
+    public void testSetPreferenceForTwoProfiles() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle2 = setupEnterpriseNetwork();
+        final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2);
+        mServiceContext.setWorkProfile(testHandle4, true);
+        registerDefaultNetworkCallbacks();
+
+        final TestNetworkCallback app4Cb = new TestNetworkCallback();
+        final int testWorkProfileAppUid4 =
+                UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID);
+        registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4);
+
+        // Connect both a regular cell agent and an enterprise network first.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+        workAgent.connect(true);
+
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+                INetd.PERMISSION_SYSTEM);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle2));
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                app4Cb);
+
+        mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle4));
+
+        app4Cb.expectAvailableCallbacksValidated(workAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                mProfileDefaultNetworkCallback);
+
+        mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle2));
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                app4Cb);
+
+        workAgent.disconnect();
+        mCellNetworkAgent.disconnect();
+
+        mCm.unregisterNetworkCallback(app4Cb);
+        // Other callbacks will be unregistered by tearDown()
+    }
+
+    @Test
+    public void testProfilePreferenceRemovedUponUserRemoved() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle = setupEnterpriseNetwork();
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+        removedIntent.putExtra(Intent.EXTRA_USER, testHandle);
+        processBroadcast(removedIntent);
+
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+    }
+
+    /**
+     * Make sure that OEM preference and per-profile preference can't be used at the same
+     * time and throw ISE if tried
+     */
+    @Test
+    public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception {
+        final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(testHandle, true);
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+        setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
+                OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY);
+        assertThrows("Should not be able to set per-profile pref while OEM prefs present",
+                IllegalStateException.class, () ->
+                        mCm.setProfileNetworkPreference(testHandle,
+                                PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                                r -> r.run(), listener));
+
+        // Empty the OEM prefs
+        final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
+        final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build();
+        mService.setOemNetworkPreference(emptyOemPref, oemPrefListener);
+        oemPrefListener.expectOnComplete();
+
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        assertThrows("Should not be able to set OEM prefs while per-profile pref is on",
+                IllegalStateException.class , () ->
+                        mService.setOemNetworkPreference(emptyOemPref, oemPrefListener));
+    }
+
+    /**
+     * Make sure wrong preferences for per-profile default networking are rejected.
+     */
+    @Test
+    public void testProfileNetworkPrefWrongPreference() throws Exception {
+        final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(testHandle, true);
+        assertThrows("Should not be able to set an illegal preference",
+                IllegalArgumentException.class,
+                () -> mCm.setProfileNetworkPreference(testHandle,
+                        PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
+    }
+
+    /**
+     * Make sure requests for per-profile default networking for a non-work profile are
+     * rejected
+     */
+    @Test
+    public void testProfileNetworkPrefWrongProfile() throws Exception {
+        final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(testHandle, false);
+        assertThrows("Should not be able to set a user pref for a non-work profile",
+                IllegalArgumentException.class , () ->
+                        mCm.setProfileNetworkPreference(testHandle,
+                                PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
     }
 }
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index a10a3c8..5ec1119 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -21,13 +21,29 @@
 
 package com.android.server
 
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_WIFI
+import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT
 import android.net.ConnectivityManager.TYPE_ETHERNET
 import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_MOBILE_CBS
+import android.net.ConnectivityManager.TYPE_MOBILE_DUN
+import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY
+import android.net.ConnectivityManager.TYPE_MOBILE_FOTA
+import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI
+import android.net.ConnectivityManager.TYPE_MOBILE_IA
+import android.net.ConnectivityManager.TYPE_MOBILE_IMS
+import android.net.ConnectivityManager.TYPE_MOBILE_MMS
 import android.net.ConnectivityManager.TYPE_MOBILE_SUPL
+import android.net.ConnectivityManager.TYPE_VPN
 import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.ConnectivityManager.TYPE_WIFI_P2P
 import android.net.ConnectivityManager.TYPE_WIMAX
+import android.net.EthernetManager
 import android.net.NetworkInfo.DetailedState.CONNECTED
 import android.net.NetworkInfo.DetailedState.DISCONNECTED
+import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.server.ConnectivityService.LegacyTypeTracker
@@ -36,7 +52,6 @@
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
@@ -52,88 +67,130 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class LegacyTypeTrackerTest {
-    private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
+    private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE,
+            TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI,
+            TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA,
+            TYPE_MOBILE_EMERGENCY, TYPE_VPN)
 
     private val mMockService = mock(ConnectivityService::class.java).apply {
         doReturn(false).`when`(this).isDefaultNetwork(any())
     }
-    private val mTracker = LegacyTypeTracker(mMockService).apply {
-        supportedTypes.forEach {
-            addSupportedType(it)
-        }
+    private val mPm = mock(PackageManager::class.java)
+    private val mContext = mock(Context::class.java).apply {
+        doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI)
+        doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+        doReturn(mPm).`when`(this).packageManager
+        doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService(
+                Context.ETHERNET_SERVICE)
+    }
+    private val mTm = mock(TelephonyManager::class.java).apply {
+        doReturn(true).`when`(this).isDataCapable
+    }
+
+    private fun makeTracker() = LegacyTypeTracker(mMockService).apply {
+        loadSupportedTypes(mContext, mTm)
     }
 
     @Test
     fun testSupportedTypes() {
-        try {
-            mTracker.addSupportedType(supportedTypes[0])
-            fail("Expected IllegalStateException")
-        } catch (expected: IllegalStateException) {}
+        val tracker = makeTracker()
         supportedTypes.forEach {
-            assertTrue(mTracker.isTypeSupported(it))
+            assertTrue(tracker.isTypeSupported(it))
         }
-        assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE))
+        assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE))
+    }
+
+    @Test
+    fun testSupportedTypes_NoEthernet() {
+        doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE)
+        assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET))
+    }
+
+    @Test
+    fun testSupportedTypes_NoTelephony() {
+        doReturn(false).`when`(mTm).isDataCapable
+        val tracker = makeTracker()
+        val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN)
+        nonMobileTypes.forEach {
+            assertTrue(tracker.isTypeSupported(it))
+        }
+        supportedTypes.toSet().minus(nonMobileTypes).forEach {
+            assertFalse(tracker.isTypeSupported(it))
+        }
+    }
+
+    @Test
+    fun testSupportedTypes_NoWifiDirect() {
+        doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+        val tracker = makeTracker()
+        assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P))
+        supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach {
+            assertTrue(tracker.isTypeSupported(it))
+        }
     }
 
     @Test
     fun testSupl() {
+        val tracker = makeTracker()
         val mobileNai = mock(NetworkAgentInfo::class.java)
-        mTracker.add(TYPE_MOBILE, mobileNai)
+        tracker.add(TYPE_MOBILE, mobileNai)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
-        mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+        tracker.add(TYPE_MOBILE_SUPL, mobileNai)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
         reset(mMockService)
-        mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
+        tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
         reset(mMockService)
-        mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+        tracker.add(TYPE_MOBILE_SUPL, mobileNai)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
         reset(mMockService)
-        mTracker.remove(mobileNai, false)
+        tracker.remove(mobileNai, false)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE)
     }
 
     @Test
     fun testAddNetwork() {
+        val tracker = makeTracker()
         val mobileNai = mock(NetworkAgentInfo::class.java)
         val wifiNai = mock(NetworkAgentInfo::class.java)
-        mTracker.add(TYPE_MOBILE, mobileNai)
-        mTracker.add(TYPE_WIFI, wifiNai)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.add(TYPE_MOBILE, mobileNai)
+        tracker.add(TYPE_WIFI, wifiNai)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Make sure adding a second NAI does not change the results.
         val secondMobileNai = mock(NetworkAgentInfo::class.java)
-        mTracker.add(TYPE_MOBILE, secondMobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.add(TYPE_MOBILE, secondMobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Make sure removing a network that wasn't added for this type is a no-op.
-        mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Remove the top network for mobile and make sure the second one becomes the network
         // of record for this type.
-        mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Make sure adding a network for an unsupported type does not register it.
-        mTracker.add(UNSUPPORTED_TYPE, mobileNai)
-        assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE))
+        tracker.add(UNSUPPORTED_TYPE, mobileNai)
+        assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE))
     }
 
     @Test
     fun testBroadcastOnDisconnect() {
+        val tracker = makeTracker()
         val mobileNai1 = mock(NetworkAgentInfo::class.java)
         val mobileNai2 = mock(NetworkAgentInfo::class.java)
         doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
-        mTracker.add(TYPE_MOBILE, mobileNai1)
+        tracker.add(TYPE_MOBILE, mobileNai1)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
         doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
-        mTracker.add(TYPE_MOBILE, mobileNai2)
+        tracker.add(TYPE_MOBILE, mobileNai2)
         verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
-        mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
+        tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE)
     }
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 1c0ba4f..ea2b362 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -40,6 +40,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkProvider;
+import android.net.NetworkScore;
 import android.os.Binder;
 import android.text.format.DateUtils;
 
@@ -355,8 +356,9 @@
         caps.addCapability(0);
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
-                new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
-                mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
+                new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
+                mCtx, null, new NetworkAgentConfig() /* config */, mConnService, mNetd,
+                mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
                 mQosCallbackTracker, new ConnectivityService.Dependencies());
         nai.everValidated = true;
         return nai;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index b8f7fbc..11fcea6 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1026,7 +1026,11 @@
             .thenReturn(new Network[] { new Network(101) });
 
         when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
-                anyInt(), any(), anyInt())).thenReturn(new Network(102));
+                any(), any(), anyInt())).thenAnswer(invocation -> {
+                    // The runner has registered an agent and is now ready.
+                    legacyRunnerReady.open();
+                    return new Network(102);
+                });
         final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
@@ -1048,7 +1052,7 @@
             ArgumentCaptor<NetworkCapabilities> ncCaptor =
                     ArgumentCaptor.forClass(NetworkCapabilities.class);
             verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
-                    lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt());
+                    lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt());
 
             // In this test the expected address is always v4 so /32.
             // Note that the interface needs to be specified because RouteInfo objects stored in
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index a0200275..814cad4 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -74,7 +74,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.server.VcnManagementService.VcnCallback;
 import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
 import com.android.server.vcn.TelephonySubscriptionTracker;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 0e5f5e4..ca6448c 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -188,7 +188,7 @@
                         any(),
                         lpCaptor.capture(),
                         ncCaptor.capture(),
-                        anyInt(),
+                        any(),
                         any(),
                         anyInt());
         verify(mIpSecSvc)